การสร้างหมายเลขลำดับแบบกระจาย?


103

โดยทั่วไปฉันได้ใช้การสร้างหมายเลขลำดับโดยใช้ลำดับฐานข้อมูลในอดีต

เช่นการใช้ Postgres SERIAL type http://www.neilconway.org/docs/sequences/

ฉันสงสัยว่าจะสร้างหมายเลขลำดับสำหรับระบบกระจายขนาดใหญ่ที่ไม่มีฐานข้อมูลได้อย่างไร ใครมีประสบการณ์หรือข้อเสนอแนะเกี่ยวกับแนวทางปฏิบัติที่ดีที่สุดในการสร้างหมายเลขลำดับในลักษณะที่ปลอดภัยสำหรับลูกค้าหลายรายหรือไม่?


คำถามนี้เก่า แต่โปรดดูคำตอบใหม่ของฉันstackoverflow.com/questions/2671858/…
Jesper M

คุณใช้ nextval.org อย่างไร? เว็บไซต์ค่อนข้างแปลกและฉันไม่รู้ว่าเกี่ยวกับอะไร มันเป็นคำสั่ง Unix หรือไม่? หรือบริการคลาวด์บ้าง?
diegosasw

คำตอบ:


116

ตกลงนี่เป็นคำถามเก่ามากซึ่งตอนนี้ฉันเห็นเป็นครั้งแรก

คุณจะต้องแยกความแตกต่างระหว่างหมายเลขลำดับและรหัสเฉพาะที่ (เป็นทางเลือก) ซึ่งสามารถจัดเรียงได้อย่างหลวม ๆ ตามเกณฑ์เฉพาะ (โดยทั่วไปคือเวลาในการสร้าง) หมายเลขลำดับที่แท้จริงบ่งบอกถึงความรู้เกี่ยวกับสิ่งที่คนงานอื่น ๆ ได้ทำและด้วยเหตุนี้จึงต้องใช้สถานะร่วมกัน ไม่มีวิธีง่ายๆในการดำเนินการในลักษณะที่กระจายและมีขนาดใหญ่ คุณสามารถตรวจสอบสิ่งต่างๆเช่นการออกอากาศเครือข่ายช่วงที่มีหน้าต่างสำหรับพนักงานแต่ละคนและตารางแฮชที่แจกจ่ายสำหรับ ID ผู้ปฏิบัติงานที่ไม่ซ้ำกันแต่ก็ทำงานได้มาก

ID ที่ไม่ซ้ำกันเป็นอีกเรื่องหนึ่งมีหลายวิธีที่ดีในการสร้าง ID เฉพาะในลักษณะที่กระจายอำนาจ:

a) คุณสามารถใช้บริการเครือข่าย Snowflake ID ของ Twitterได้ เกล็ดหิมะคือ:

  • บริการเครือข่ายคือคุณโทรผ่านเครือข่ายเพื่อรับรหัสเฉพาะ
  • ซึ่งสร้าง ID เฉพาะ 64 บิตที่เรียงลำดับตามเวลาในการสร้าง
  • และบริการสามารถปรับขนาดได้สูงและ (อาจ) พร้อมใช้งานสูง แต่ละอินสแตนซ์สามารถสร้างได้หลายพัน ID ต่อวินาทีและคุณสามารถรันได้หลายอินสแตนซ์บน LAN / WAN ของคุณ
  • เขียนใน Scala ทำงานบน JVM

b) คุณสามารถสร้าง ID เฉพาะบนไคลเอนต์ได้เองโดยใช้วิธีการที่ได้มาจากวิธีการสร้าง UUIDและรหัสของ Snowflake มีหลายตัวเลือก แต่มีบางอย่างตามแนวของ:

  • 40 บิตที่สำคัญที่สุด: การประทับเวลา; เวลาในการสร้างรหัส (เรากำลังใช้บิตที่สำคัญที่สุดสำหรับการประทับเวลาเพื่อให้ ID สามารถจัดเรียงได้ตามเวลาในการสร้าง)

  • 14 บิตถัดไป: ตัวนับต่อตัวกำเนิดซึ่งแต่ละตัวสร้างจะเพิ่มขึ้นทีละหนึ่งสำหรับ ID ใหม่ที่สร้างขึ้น เพื่อให้แน่ใจว่า ID ที่สร้างขึ้นในช่วงเวลาเดียวกัน (การประทับเวลาเดียวกัน) จะไม่ทับซ้อนกัน

  • 10 บิตสุดท้ายหรือมากกว่านั้น: ค่าเฉพาะสำหรับเครื่องกำเนิดไฟฟ้าแต่ละตัว เมื่อใช้สิ่งนี้เราไม่จำเป็นต้องทำการซิงโครไนซ์ระหว่างเครื่องกำเนิดไฟฟ้า (ซึ่งยากมาก) เนื่องจากเครื่องกำเนิดไฟฟ้าทั้งหมดสร้าง ID ที่ไม่ทับซ้อนกันเนื่องจากค่านี้

c) คุณสามารถสร้าง ID บนไคลเอนต์โดยใช้เพียงการประทับเวลาและค่าสุ่ม สิ่งนี้หลีกเลี่ยงความจำเป็นในการรู้จักเครื่องกำเนิดไฟฟ้าทั้งหมดและกำหนดค่าที่ไม่ซ้ำกันของเครื่องกำเนิดไฟฟ้าแต่ละเครื่อง ในทางกลับกัน ID ดังกล่าวไม่รับประกันว่าจะไม่ซ้ำกันทั่วโลก แต่มีแนวโน้มสูงมากที่จะไม่ซ้ำกัน (ในการชนกันเครื่องกำเนิดไฟฟ้าอย่างน้อยหนึ่งตัวจะต้องสร้างค่าสุ่มเดียวกันในเวลาเดียวกัน) บางอย่างตามแนวของ:

  • 32 บิตที่สำคัญที่สุด: Timestamp เวลาในการสร้าง ID
  • 32 บิตที่มีนัยสำคัญน้อยที่สุด: การสุ่ม 32 บิตสร้างขึ้นใหม่สำหรับแต่ละ ID

ง) วิธีที่ง่ายออกใช้ UUIDs / guid ของ


Cassandra รองรับเคาน์เตอร์ ( cassandra.apache.org/doc/cql3/CQL.html#counters ) มีข้อ จำกัด บางประการ
Piyush Kansal

หมายเลขลำดับนั้นง่ายต่อการกำหนดตำแหน่งสำหรับดัชนีบิตแมป แต่บางครั้งรหัสที่ไม่ซ้ำกันก็ยาวเกินไป (64 บิตหรือ 128 บิต) การแมป ID เฉพาะกับตำแหน่งดัชนีบิตแมปได้อย่างไร ขอบคุณ.
brucenan

2
ตัวเลือกที่ชอบมาก #b ..... มันสามารถอนุญาตให้มีขนาดสูงและไม่ก่อให้เกิดปัญหาพร้อมกันมากนัก
puneet

2
twitter/snowflakeไม่ได้รับการดูแลอีกต่อไป
Navin

หากคุณต้องการใช้ Apache2 Licensed ของตัวเลือก B โปรดดูbitbucket.org/pythagorasio/common-libraries/src/master/…นอกจากนี้คุณยังสามารถรับได้จาก maven io.pythagoras.common: distribution-ลำดับ-id-generator: 1.0 .0
Wpigott

16

ตอนนี้มีตัวเลือกมากขึ้น

แม้ว่าคำถามนี้จะ "เก่า" แต่ฉันก็มาถึงที่นี่ดังนั้นฉันคิดว่าอาจเป็นประโยชน์ที่จะปล่อยให้ตัวเลือกที่ฉันรู้จัก (จนถึงตอนนี้):

  • คุณอาจจะลองHazelcast ในรุ่น 1.9 จะมีการใช้งาน java.util.concurrent AtomicLong แบบกระจาย
  • นอกจากนี้คุณยังสามารถใช้Zookeeper มีวิธีการสร้างโหนดลำดับ (ต่อท้ายชื่อ znode แม้ว่าฉันจะชอบใช้หมายเลขเวอร์ชันของโหนด) ระวังตัวนี้ด้วย: หากคุณไม่ต้องการหมายเลขที่ไม่ได้รับในลำดับอาจไม่ใช่สิ่งที่คุณต้องการ

ไชโย


3
Zookeeper เป็นตัวเลือกที่ฉันใช้มีคำอธิบายที่ดีและเขียนสิ่งนี้ไว้ในรายชื่อผู้รับจดหมายที่ฉันเริ่ม - mail-archive.com/zookeeper-user@hadoop.apache.org/msg01967.html
จอน

จอนขอบคุณที่ชี้ไปที่หัวข้อนั้นนั่นคือประเภทของวิธีแก้ปัญหาที่ฉันคิด BTW คุณสร้างรหัสเพื่อเอาชนะข้อ จำกัด MAX_INT หรือไม่?
เปาโล

15

คุณสามารถให้แต่ละโหนดมี ID ที่ไม่ซ้ำกัน (ซึ่งคุณอาจมีอยู่แล้ว) จากนั้นนำหน้านั้นไปยังหมายเลขลำดับ

ตัวอย่างเช่นโหนด 1 สร้างลำดับ 001-00001 001-00002 001-00003 เป็นต้นและโหนด 5 สร้าง 005-00001 005-00002

ไม่ซ้ำกัน :-)

อีกวิธีหนึ่งหากคุณต้องการระบบรวมศูนย์คุณอาจพิจารณาให้เซิร์ฟเวอร์ลำดับของคุณแจกแจงเป็นบล็อก ซึ่งจะช่วยลดค่าใช้จ่ายได้มาก ตัวอย่างเช่นแทนที่จะขอ ID ใหม่จากเซิร์ฟเวอร์กลางสำหรับแต่ละ ID ที่ต้องกำหนดคุณขอ ID ในบล็อก 10,000 จากเซิร์ฟเวอร์กลางจากนั้นจะต้องทำคำขอเครือข่ายอื่นเมื่อคุณหมดเท่านั้น


1
ฉันชอบประเด็นของคุณเกี่ยวกับการสร้างรหัสแบทช์ แต่เพียงแค่จำกัดความเป็นไปได้ในการคำนวณตามเวลาจริง
ishan

ฉันได้ใช้กลไกที่คล้ายกัน นอกเหนือจากไคลเอนต์ที่แคชบล็อกของลำดับแล้วฉันยังได้เพิ่มเซิร์ฟเวอร์โฮสต์หลายตัวที่แคชบล็อกของลำดับ เครื่องกำเนิดไฟฟ้าหลัก (ตัวเดียว) ได้รับการบำรุงรักษาในหน่วยเก็บข้อมูลที่มีอยู่สูงหรือโฮสต์หลักเดียวซึ่งสามารถเข้าถึงได้เฉพาะกลุ่มเซิร์ฟเวอร์โฮสต์เท่านั้น การแคชเซิร์ฟเวอร์จะช่วยให้เรามีเวลาทำงานมากขึ้นในการสร้างแรงบันดาลใจให้มาสเตอร์คนเดียวลงไปชั่วขณะ
Janakiram

11

ก็สามารถทำได้ด้วยRedisson มันใช้เวอร์ชันที่กระจายและปรับขนาดได้ของAtomicLong. นี่คือตัวอย่าง:

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

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

8

ถ้ามันต้องเรียงตามลำดับกันทั่วโลกจริงๆไม่ใช่แค่ไม่ซ้ำกันฉันจะพิจารณาสร้างบริการง่ายๆเพียงรายการเดียวสำหรับจ่ายหมายเลขเหล่านี้

ระบบแบบกระจายต้องอาศัยบริการเล็ก ๆ น้อย ๆ ที่โต้ตอบกันและสำหรับงานง่ายๆแบบนี้คุณต้องการหรือจะได้รับประโยชน์จากโซลูชันแบบกระจายที่ซับซ้อนอื่น ๆ หรือไม่?


3
... และจะเกิดอะไรขึ้นเมื่อเซิร์ฟเวอร์ที่ใช้บริการนั้นหยุดทำงาน
Navin

มีการแจ้งเตือนที่บอกให้ใครบางคนเริ่มต้นใหม่หรือไม่? บางครั้งก็จะดี ฉันคิดว่าคำตอบคือพยายามพูดว่า "เก็บสิ่งต่างๆไว้ในมุมมอง" โซลูชันแบบกระจายที่สมบูรณ์แบบมีข้อเสียของตัวเองและบางครั้งก็ง่ายกว่านั้น
nic ferrier

6

มีกลยุทธ์บางอย่าง แต่ไม่มีสิ่งใดที่ฉันรู้ว่าสามารถแจกจ่ายและให้ลำดับที่แท้จริงได้

  1. มีเครื่องกำเนิดไฟฟ้าหมายเลขกลาง ไม่จำเป็นต้องเป็นฐานข้อมูลขนาดใหญ่ memcachedมีตัวนับอะตอมที่รวดเร็วในกรณีส่วนใหญ่มันเร็วพอสำหรับทั้งคลัสเตอร์ของคุณ
  2. แยกช่วงจำนวนเต็มสำหรับแต่ละโหนด (เช่นคำตอบของ Steven Schlanskter )
  3. ใช้ตัวเลขสุ่มหรือ UUID
  4. ใช้ข้อมูลบางส่วนร่วมกับ ID ของโหนดและแฮชทั้งหมด (หรือhmac )

โดยส่วนตัวฉันจะเรียนรู้ UUIDs หรือ memcached ถ้าฉันต้องการมีพื้นที่ที่ต่อเนื่องกันเป็นส่วนใหญ่


5

ทำไมไม่ใช้เครื่องกำเนิด UUID (เธรดปลอดภัย)

ฉันควรจะขยายเรื่องนี้

UUID ได้รับการรับรองว่าไม่ซ้ำกันทั่วโลก (หากคุณหลีกเลี่ยง UUID ตามตัวเลขสุ่มซึ่งความเป็นเอกลักษณ์นั้นมีความเป็นไปได้สูง)

เป็นไปตามข้อกำหนด "แบบกระจาย" ของคุณไม่ว่าคุณจะใช้เครื่องกำเนิด UUID จำนวนเท่าใดก็ตามโดยความเป็นเอกลักษณ์สากลของแต่ละ UUID

คุณสามารถปฏิบัติตามข้อกำหนด "เธรดปลอดภัย" ได้โดยเลือกเครื่องกำเนิด UUID "เธรดปลอดภัย"

ข้อกำหนด "หมายเลขลำดับ" ของคุณจะถือว่าเป็นไปตามความเป็นเอกลักษณ์สากลที่รับประกันของ UUID แต่ละรายการ

โปรดทราบว่าการใช้หมายเลขลำดับฐานข้อมูลจำนวนมาก (เช่น Oracle) ไม่รับประกันว่าจะเพิ่มขึ้นอย่างจำเจหรือ (แม้) การเพิ่มหมายเลขลำดับ (ตาม "การเชื่อมต่อ") เนื่องจากชุดหมายเลขลำดับที่ต่อเนื่องกันได้รับการจัดสรรในบล็อก "แคช" ตามการเชื่อมต่อ สิ่งนี้รับประกันความเป็นเอกลักษณ์ระดับโลกและรักษาความเร็วที่เพียงพอ แต่หมายเลขลำดับที่จัดสรรจริง (เมื่อเวลาผ่านไป) สามารถสับสนได้เมื่อมีการจัดสรรโดยการเชื่อมต่อหลายรายการ!


1
ในขณะที่ UUID ทำงาน แต่ปัญหาก็คือคุณต้องระมัดระวังวิธีการจัดเก็บหากคุณต้องทำดัชนีคีย์ที่สร้างขึ้นในที่สุด โดยทั่วไปแล้วพวกเขาจะใช้พื้นที่มากกว่าลำดับที่เพิ่มขึ้นอย่างจำเจ โปรดดูpercona.com/blog/2014/12/19/store-uuid-optimized-wayสำหรับการสนทนาเกี่ยวกับการจัดเก็บด้วย MySQL
Pavel

2

การสร้าง ID แบบกระจายสามารถเก็บถาวรด้วย Redis และ Lua การดำเนินงานที่มีอยู่ในGithub มันสร้างรหัสเฉพาะที่กระจายและเรียงลำดับได้


2

ฉันรู้ว่านี่เป็นคำถามเก่า แต่เราก็เผชิญกับความต้องการเดียวกันและไม่สามารถหาวิธีแก้ปัญหาที่ตอบสนองความต้องการของเราได้ ความต้องการของเราคือการได้รับลำดับเฉพาะ (0,1,2,3 ... n) ของรหัสและด้วยเหตุนี้เกล็ดหิมะจึงไม่ได้ช่วยอะไร เราสร้างระบบของเราเองเพื่อสร้างรหัสโดยใช้ Redis Redis เป็นเธรดเดียวดังนั้นกลไกรายการ / คิวจะให้ป๊อป 1 ครั้งต่อครั้ง

สิ่งที่เราทำคือเราสร้างบัฟเฟอร์ของรหัสโดยเริ่มแรกคิวจะมี 0 ถึง 20 รหัสที่พร้อมส่งเมื่อได้รับการร้องขอ ไคลเอนต์หลายรายสามารถขอ id และ redis จะแสดงทีละ 1 id หลังจากที่ทุกป๊อปจากซ้ายเราจะแทรก BUFFER + currentId ไปทางขวาซึ่งจะทำให้รายการบัฟเฟอร์ดำเนินต่อไป การติดตั้งที่นี่


0

ฉันได้เขียนบริการง่ายๆซึ่งสามารถสร้างตัวเลขยาว 64 บิตแบบกึ่งไม่ซ้ำกันที่ไม่ใช่ลำดับ สามารถติดตั้งบนเครื่องหลายเครื่องเพื่อความซ้ำซ้อนและความยืดหยุ่น ใช้ ZeroMQ สำหรับการส่งข้อความ สำหรับข้อมูลเพิ่มเติมเกี่ยวกับวิธีการทำงานดูที่หน้า github: zUID


0

การใช้ฐานข้อมูลคุณสามารถเพิ่มได้ถึง 1.000+ ครั้งต่อวินาทีด้วยแกนเดียว มันค่อนข้างง่าย คุณสามารถใช้ฐานข้อมูลของตัวเองเป็นแบ็กเอนด์เพื่อสร้างหมายเลขนั้นได้ (เนื่องจากควรเป็นข้อมูลรวมของตัวเองในรูปแบบ DDD)

ฉันมีสิ่งที่ดูเหมือนจะเป็นปัญหา ฉันมีหลายพาร์ติชันและฉันต้องการได้ตัวนับชดเชยสำหรับแต่ละพาร์ติชั่น ฉันใช้สิ่งนี้:

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

จากนั้นดำเนินการคำสั่งต่อไปนี้:

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

หากแอปพลิเคชันของคุณอนุญาตคุณสามารถจัดสรรบล็อกพร้อมกันได้ (นั่นคือกรณีของฉัน)

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

หากคุณต้องการปริมาณงานเพิ่มเติมและไม่สามารถจัดสรรค่าชดเชยล่วงหน้าได้คุณสามารถใช้บริการของคุณเองโดยใช้ Flink สำหรับการประมวลผลแบบเรียลไทม์ ฉันสามารถเพิ่มครั้งละประมาณ 100K ต่อพาร์ติชัน

หวังว่าจะช่วยได้!


0

ปัญหาคล้ายกับ: ในโลก iscsi ซึ่งแต่ละ luns / volume จะต้องระบุไม่ซ้ำกันโดยผู้ริเริ่มที่ทำงานบนฝั่งไคลเอ็นต์ มาตรฐาน iscsi กล่าวว่าสองสามบิตแรกต้องเป็นตัวแทนของข้อมูลผู้ให้บริการ / ผู้ผลิตที่เก็บข้อมูลและส่วนที่เหลือเพิ่มขึ้นอย่างจำเจ

ในทำนองเดียวกันเราสามารถใช้บิตเริ่มต้นในระบบแบบกระจายของโหนดเพื่อแสดงถึง nodeID และส่วนที่เหลือสามารถเพิ่มขึ้นอย่างจำเจ


1
โปรดเพิ่มรายละเอียดเพิ่มเติม
Ved Prakash

0

วิธีแก้ปัญหาอย่างหนึ่งที่ดีคือการใช้การสร้างตามเวลาที่ยาวนาน สามารถทำได้ด้วยการสำรองฐานข้อมูลแบบกระจาย

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.