Hệ số thứ tự phân tán?


103

Trước đây, tôi thường triển khai tạo số thứ tự bằng cách sử dụng chuỗi cơ sở dữ liệu.

ví dụ: Sử dụng loại Postgres SERIAL http://www.neilconway.org/docs/sequences/

Tôi rất tò mò về cách tạo số thứ tự cho các hệ thống phân tán lớn nơi không có cơ sở dữ liệu. Có ai có kinh nghiệm hoặc đề xuất về phương pháp hay nhất để đạt được việc tạo số thứ tự theo cách an toàn cho nhiều khách hàng không?


Câu hỏi này đã cũ, nhưng vui lòng xem câu trả lời mới của tôi stackoverflow.com/questions/2671858/…
Jesper M

Bạn sử dụng nextval.org như thế nào? Trang web hơi lạ và tôi không biết về cái gì. Nó là một số lệnh Unix? Hoặc một số dịch vụ đám mây?
diegosasw

Câu trả lời:


116

OK, đây là một câu hỏi rất cũ, mà bây giờ tôi mới thấy lần đầu tiên.

Bạn sẽ cần phân biệt giữa số thứ tựID duy nhất có thể sắp xếp lỏng lẻo (tùy chọn) theo một tiêu chí cụ thể (thường là thời gian tạo). Số thứ tự thực sự ngụ ý kiến ​​thức về những gì tất cả những người lao động khác đã làm và như vậy yêu cầu trạng thái được chia sẻ. Không có cách nào dễ dàng để làm điều này một cách phân tán, quy mô cao. Bạn có thể xem xét những thứ như phát sóng mạng, phạm vi cửa sổ cho từng công nhân và bảng băm được phân phối cho các ID công nhân duy nhất , nhưng còn rất nhiều việc.

ID duy nhất là một vấn đề khác, có một số cách tốt để tạo ID duy nhất theo cách phi tập trung:

a) Bạn có thể sử dụng dịch vụ mạng Snowflake ID của Twitter . Snowflake là một:

  • Dịch vụ nối mạng, tức là bạn thực hiện cuộc gọi mạng để lấy một ID duy nhất;
  • tạo ra các ID duy nhất 64 bit được sắp xếp theo thời gian tạo;
  • và dịch vụ có khả năng mở rộng cao và (có khả năng) khả dụng cao; mỗi phiên bản có thể tạo ra nhiều nghìn ID mỗi giây và bạn có thể chạy nhiều phiên bản trên mạng LAN / WAN của mình;
  • được viết bằng Scala, chạy trên JVM.

b) Bạn có thể tạo các ID duy nhất trên chính các máy khách, sử dụng phương pháp tiếp cận dựa trên cách tạo UUID và ID của Snowflake. Có nhiều tùy chọn, nhưng một số tùy chọn dọc theo:

  • 40 bit quan trọng nhất hoặc lâu hơn: Dấu thời gian; thời gian tạo ID. (Chúng tôi đang sử dụng các bit quan trọng nhất cho dấu thời gian để làm cho các ID có thể sắp xếp theo thời gian tạo.)

  • 14 bit tiếp theo hoặc lâu hơn: Bộ đếm cho mỗi bộ tạo, mỗi bộ tạo tăng một cho mỗi ID mới được tạo. Điều này đảm bảo rằng các ID được tạo tại cùng một thời điểm (cùng dấu thời gian) không trùng lặp.

  • Khoảng 10 bit cuối cùng: Một giá trị duy nhất cho mỗi bộ tạo. Sử dụng điều này, chúng tôi không cần phải thực hiện bất kỳ đồng bộ hóa nào giữa các trình phát (điều này cực kỳ khó), vì tất cả các trình tạo đều tạo ra các ID không chồng chéo vì giá trị này.

c) Bạn có thể tạo ID trên các máy khách, chỉ sử dụng dấu thời gian và giá trị ngẫu nhiên. Điều này tránh sự cần thiết phải biết tất cả các trình tạo và gán cho mỗi trình tạo một giá trị duy nhất. Mặt khác, những ID như vậy không được đảm bảo là duy nhất trên toàn cầu, chúng chỉ có khả năng rất cao là duy nhất. (Để va chạm, một hoặc nhiều trình tạo sẽ phải tạo cùng một giá trị ngẫu nhiên chính xác tại cùng một thời điểm.)

  • 32 bit quan trọng nhất: Dấu thời gian, thời gian tạo ID.
  • 32 bit ít quan trọng nhất: 32 bit ngẫu nhiên, được tạo mới cho mỗi ID.

d) Cách dễ dàng, sử dụng UUID / GUID .


Cassandra hỗ trợ bộ đếm ( cassandra.apache.org/doc/cql3/CQL.html#counters ), tuy nhiên vẫn có một số hạn chế.
Piyush Kansal

số thứ tự dễ dàng thiết lập vị trí cho chỉ mục bitmap, nhưng id duy nhất đôi khi quá dài (64bit hoặc 128bit), làm thế nào để ánh xạ id duy nhất tới vị trí chỉ mục bitmap? Cảm ơn.
brucenan

2
thực sự thích lựa chọn #b ..... nó có thể cho phép quy mô cao và không gây ra nhiều vấn đề đồng thời
Puneet

2
twitter/snowflakekhông còn được duy trì
Navin

Nếu bạn muốn triển khai Apache2 được cấp phép của tùy chọn B, hãy xem bitbucket.org/pythagorasio/common-libraries/src/master/… Bạn cũng có thể tải nó từ maven io.pythagoras.common: Distrib-serial-id -vator: 1.0 .0
Wpigott

16

Bây giờ có nhiều lựa chọn hơn.

Mặc dù câu hỏi này "cũ", tôi đã hiểu ở đây, vì vậy tôi nghĩ có thể hữu ích nếu để lại các tùy chọn mà tôi biết (cho đến nay):

  • Bạn có thể thử Hazelcast . Trong bản phát hành 1.9, nó bao gồm triển khai Phân tán của java.util.concurrent.AtomicLong
  • Bạn cũng có thể sử dụng Zookeeper . Nó cung cấp các phương pháp để tạo các nút trình tự (được nối với tên znode, mặc dù tôi thích sử dụng số phiên bản của các nút hơn). Tuy nhiên, hãy cẩn thận với điều này: nếu bạn không muốn các số bị bỏ lỡ trong dãy số của mình, nó có thể không phải là những gì bạn muốn.

Chúc mừng


3
Zookeeper là lựa chọn mà tôi đã chọn, có một mô tả hay và viết về điều này trong danh sách gửi thư mà tôi đã bắt đầu - mail-archive.com/zookeeper-user@hadoop.apache.org/msg01967.html
Jon

Jon, cảm ơn vì đã chỉ vào chủ đề đó, đó chính xác là loại giải pháp mà tôi đang nghĩ. BTW, bạn đã tạo mã để vượt qua giới hạn MAX_INT chưa?
Paolo

15

Bạn có thể có mỗi nút có một ID duy nhất (mà bạn có thể có) và sau đó thêm nó vào số thứ tự.

Ví dụ: nút 1 tạo chuỗi 001-00001 001-00002 001-00003, v.v. và nút 5 tạo 005-00001 005-00002

Độc nhất :-)

Ngoài ra, nếu bạn muốn một số loại hệ thống tập trung, bạn có thể xem xét việc để máy chủ tuần tự của bạn cung cấp theo khối. Điều này làm giảm chi phí đáng kể. Ví dụ: thay vì yêu cầu ID mới từ máy chủ trung tâm cho mỗi ID phải được chỉ định, bạn yêu cầu ID theo khối 10.000 từ máy chủ trung tâm và sau đó chỉ phải thực hiện một yêu cầu mạng khác khi hết.


1
tôi thích quan điểm của bạn về việc tạo id lô, nhưng nó chỉ giới hạn bất kỳ khả năng tính toán thời gian thực nào.
ishan

Tôi đã thực hiện một cơ chế tương tự. Trong đó, ngoài các máy khách lưu vào bộ nhớ đệm một khối chuỗi, tôi đã thêm một số máy chủ lưu trữ bộ đệm các khối chuỗi. Một trình tạo chính (duy nhất) được duy trì trong một số bộ lưu trữ có khả năng cao hoặc một máy chủ chính duy nhất, chỉ nhóm máy chủ có thể truy cập được. Bộ nhớ đệm của máy chủ cũng sẽ giúp chúng tôi có nhiều thời gian hoạt động hơn mặc dù một cái chính gặp sự cố trong giây lát.
Janakiram

11

Nó có thể được thực hiện với Redisson . Nó thực hiện phiên bản phân phối và có thể mở rộng của AtomicLong. Đây là ví dụ:

Config config = new Config();
config.addAddress("some.server.com:8291");

Redisson redisson = Redisson.create(config);
RAtomicLong atomicLong = redisson.getAtomicLong("anyAtomicLong");
atomicLong.incrementAndGet();

8

Nếu nó thực sự phải tuần tự trên toàn cầu, và không chỉ đơn giản là duy nhất, thì tôi sẽ xem xét việc tạo một dịch vụ đơn giản, đơn giản để phân phối những con số này.

Các hệ thống phân tán dựa vào rất nhiều dịch vụ nhỏ tương tác và đối với loại tác vụ đơn giản này, bạn có thực sự cần hay thực sự sẽ được lợi từ một số giải pháp phân tán, phức tạp khác?


3
... và điều gì sẽ xảy ra khi máy chủ chạy dịch vụ đó gặp sự cố?
Navin

Có một cảnh báo cho biết ai đó bắt đầu một cái khác? Đôi khi điều đó sẽ tốt. Tôi nghĩ câu trả lời là cố gắng nói "giữ mọi thứ trong quan điểm". Giải pháp phân tán hoàn hảo có những nhược điểm riêng của nó và đôi khi đơn giản hơn lại tốt hơn.
nic ferrier

6

Có một vài chiến lược; nhưng không cái nào mà tôi biết có thể thực sự được phân phối và đưa ra một chuỗi thực.

  1. có bộ tạo số trung tâm. nó không cần phải là một cơ sở dữ liệu lớn. memcachedcó bộ đếm nguyên tử nhanh, trong đại đa số trường hợp, nó đủ nhanh cho toàn bộ cụm của bạn.
  2. tách một dải số nguyên cho mỗi nút (như câu trả lời của Steven Schlanskter )
  3. sử dụng số ngẫu nhiên hoặc UUID
  4. sử dụng một số phần dữ liệu, cùng với ID của nút, và băm nó tất cả (hoặc HMAC nó)

cá nhân, tôi sẽ dựa vào UUID hoặc memcached nếu tôi muốn có một không gian gần như liền kề.


5

Tại sao không sử dụng trình tạo UUID (an toàn luồng)?

Tôi có lẽ nên mở rộng về điều này.

UUID được đảm bảo là duy nhất trên toàn cầu (nếu bạn tránh những UUID dựa trên các số ngẫu nhiên, trong đó tính duy nhất rất có thể xảy ra).

Yêu cầu "phân phối" của bạn được đáp ứng, bất kể bạn sử dụng bao nhiêu trình tạo UUID, bởi tính duy nhất toàn cầu của mỗi UUID.

Yêu cầu "an toàn luồng" của bạn có thể được đáp ứng bằng cách chọn trình tạo UUID "an toàn cho luồng".

Yêu cầu "số thứ tự" của bạn được giả định là được đáp ứng bởi tính duy nhất toàn cầu được đảm bảo của mỗi UUID.

Lưu ý rằng nhiều triển khai số thứ tự cơ sở dữ liệu (ví dụ: Oracle) không đảm bảo số thứ tự tăng đơn điệu hoặc (thậm chí) tăng (trên cơ sở mỗi "kết nối"). Điều này là do một loạt số thứ tự liên tiếp được phân bổ trong các khối "được lưu trong bộ nhớ cache" trên cơ sở mỗi kết nối. Điều này đảm bảo tính duy nhất trên toàn cầu duy trì tốc độ thích hợp. Nhưng các số thứ tự thực sự được phân bổ (theo thời gian) có thể bị lộn xộn khi được phân bổ bởi nhiều kết nối!


1
Trong khi UUID hoạt động, vấn đề với chúng là bạn phải cẩn thận cách lưu trữ chúng nếu cuối cùng bạn cần lập chỉ mục các khóa đã tạo. Chúng cũng thường sẽ chiếm nhiều không gian hơn một trình tự tăng đơn điệu. Xem percona.com/blog/2014/12/19/store-uuid-optimized-way để thảo luận về việc lưu trữ chúng bằng MySQL.
Pavel

2

Tạo ID phân tán có thể được lưu trữ với Redis và Lua. Việc triển khai có sẵn trong Github . Nó tạo ra một id duy nhất phân tán và k có thể sắp xếp.


2

Tôi biết đây là một câu hỏi cũ nhưng chúng tôi cũng đang phải đối mặt với nhu cầu tương tự và không thể tìm ra giải pháp đáp ứng nhu cầu của chúng tôi. Yêu cầu của chúng tôi là nhận được một chuỗi id duy nhất (0,1,2,3 ... n) và do đó bông tuyết không giúp được gì. Chúng tôi đã tạo hệ thống của riêng mình để tạo id bằng Redis. Redis là một luồng đơn do đó cơ chế danh sách / hàng đợi của nó sẽ luôn cung cấp cho chúng tôi 1 cửa sổ bật lên tại một thời điểm.

Những gì chúng tôi làm là, Chúng tôi tạo một bộ đệm các id, Ban đầu, hàng đợi sẽ có từ 0 đến 20 id sẵn sàng được gửi đi khi được yêu cầu. Nhiều khách hàng có thể yêu cầu một id và redis sẽ bật ra 1 id tại một thời điểm, Sau mỗi lần bật từ trái, chúng tôi chèn BUFFER + currentId vào bên phải, Điều này giữ cho danh sách đệm tiếp tục. Thực hiện tại đây


0

Tôi đã viết một dịch vụ đơn giản có thể tạo các số dài 64 bit không tuần tự bán duy nhất. Nó có thể được triển khai trên nhiều máy để dự phòng và khả năng mở rộng. Nó sử dụng ZeroMQ để nhắn tin. Để biết thêm thông tin về cách nó hoạt động, hãy xem trang github: zUID


0

Sử dụng cơ sở dữ liệu, bạn có thể đạt hơn 1.000 bước tăng mỗi giây với một lõi duy nhất. Nó là khá dễ dàng. Bạn có thể sử dụng cơ sở dữ liệu của riêng nó làm phần phụ trợ để tạo số đó (vì nó phải là tổng hợp của riêng nó, theo điều kiện DDD).

Tôi đã có những gì dường như một vấn đề tương tự. Tôi có một số phân vùng và tôi muốn có một bộ đếm bù đắp cho mỗi phân vùng. Tôi đã triển khai một cái gì đó như thế này:

CREATE DATABASE example;
USE example;
CREATE TABLE offsets (partition INTEGER, offset LONG, PRIMARY KEY (partition));
INSERT offsets VALUES (1,0);

Sau đó thực hiện câu lệnh sau:

SELECT @offset := offset from offsets WHERE partition=1 FOR UPDATE;
UPDATE offsets set offset=@offset+1 WHERE partition=1;

Nếu ứng dụng của bạn cho phép bạn, bạn có thể phân bổ một khối ngay lập tức (đó là trường hợp của tôi).

SELECT @offset := offset from offsets WHERE partition=1 FOR UPDATE;
UPDATE offsets set offset=@offset+100 WHERE partition=1;

Nếu bạn cần thêm thông lượng, không thể phân bổ hiệu số trước, bạn có thể triển khai dịch vụ của riêng mình bằng cách sử dụng Flink để xử lý thời gian thực. Tôi đã có thể nhận được khoảng 100K gia số cho mỗi phân vùng.

Hy vọng nó giúp!


0

Vấn đề tương tự như: Trong thế giới iscsi, nơi mỗi luns / volume phải được nhận dạng duy nhất bởi trình khởi tạo chạy ở phía máy khách. Tiêu chuẩn iscsi nói rằng một số bit đầu tiên phải đại diện cho thông tin nhà cung cấp / nhà sản xuất Bộ nhớ và phần còn lại đơn điệu tăng lên.

Tương tự, người ta có thể sử dụng các bit ban đầu trong hệ thống phân tán của các nút để đại diện cho nodeID và phần còn lại có thể được tăng đơn điệu.


1
xin vui lòng thêm một số chi tiết
Ved Prakash

0

Một giải pháp tốt là sử dụng một thế hệ dựa trên thời gian dài. Nó có thể được thực hiện với sự hỗ trợ của cơ sở dữ liệu phân tán.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.