English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

جمع طرق إنشاء ID عالمي غير متكرر باستخدام Java Code

عند تطوير نظام الخادم، من أجل التكيف مع طلبات البيانات الكبيرة، نحن نحتاج غالبًا إلى تخزين البيانات بشكل متسلسل، خاصة عند تطوير النظام الموزع، في هذه الحالة لا يمكننا انتظار إرجاع إشارة التكامل من قاعدة البيانات للحصول على IDs التلقائية، بل يجب توليد IDs الفريدة العالمية قبل إدراجها في قاعدة البيانات، يمكن استخدام IDs الفريدة العالمية في خوادم الألعاب لتحقيق مرونة في التجميع، مما يمنع ظهور تعارضات المفاتيح. يمكن أيضًا تحقيق تقسيم قاعدة البيانات في حالة نمو الأعمال، مثل أن يتم تخزين ممتلكات المستخدم في نفس الشريحة، ويمكن تحديد الشريحة هذه بناءً على نطاق IDs للمستخدم، مثل أن يتم تخزين IDs للمستخدمين الذين يتجاوزون 1000 ويتقلصون عن 100000 في نفس الشريحة. في الوقت الحالي، هناك أنواع عدة مستخدمة عادةً:

1، UUID المدمجة في Java

UUID.randomUUID().toString()، يمكن توليدها عبر البرنامج المخدم، ولا تعتمد توليد IDs على تنفيذ قاعدة البيانات.

المزايا:

يتم توليد IDs محليًا، ولا يتطلب أي استدعاءات عن بُعد.

فريد عالمي غير مكرر.

قوة التوسع الأفقية ممتازة.

العيوب:

يحتوي IDs على 128 bits، مما يشغل مساحة كبيرة، ويجب أن يتم تخزينها كنوع نصي، مما يؤدي إلى انخفاض كفاءة البحث.

لا يحتوي IDs المولد على Timestamp، لذا لا يمكن ضمان الزيادة التدريجية، مما يجعل الاعتماد عليه في تقسيم قاعدة البيانات غير جيد.

2، طريقة incr القائمة على Redis

Redis هو عملية متسلسلة، بينما يضمن incr عملية زيادة أتمتة. كما يدعم إعداد خطوات الزيادة.

المزايا:

التثبيت سهل، والاستخدام بسيط، ويكفي فقط التطبيق API الخاص بـ redis.

يمكن لمتعدد الخوادم مشاركة نفس خدمة Redis، مما يقلل من وقت تطوير البيانات المشتركة.

يمكن توزيع Redis بشكل جماعي لحل مشكلة الفشل النقدي.

العيوب:

إذا كانت النظام كبيرًا جدًا، فإن العديد من الخدمات تتطلب من Redis الحصول على IDs في نفس الوقت، مما يؤدي إلى نقطة ضعف في الأداء.

3، الحلول من Flicker

هذه الطريقة تعتمد على ID التكامل التلقائي في قاعدة البيانات، وهي تستخدم قاعدة بيانات منفصلة لإنشاء IDs. يمكن للجميع البحث عن المزيد من التفاصيل عبر الإنترنت، وأعتقد أنها معقدة جدًا، وأنا لا أوصي باستخدامها.

4، Twitter Snowflake

snowflake هي خوارزمية توليد IDs الموزعة المفتوحة المصدر من قبل Twitter، وفكرتها الأساسية هي: توليد ID من نوع long، باستخدام 41bit كعدد مئوية، 10bit كرقم الماكينة، و12bit كرقم التسلسل داخل المئوية. يمكن لهذه الخوارزمية توليد ما يصل إلى 1000*(2^12) IDs في الثانية الواحدة، أي حوالي 4M IDs، مما يكفي تمامًا لاحتياجات الأعمال.

باستخدام فكرة خوارزمية snowflake، يمكننا توليد IDات عالمية فريدة بناءً على سيناريوهات أعمالنا. لأن طول نوع long في Java هو 64bits، لذا يجب أن يبقى تصميمنا للـ ID تحت 64bits.

Advantages: High performance, low latency; independent application; ordered by time.

Disadvantages: Requires independent development and deployment.

For example, the ID we designed includes the following information:

| 41 bits: Timestamp | 3 bits: Region | 10 bits: Machine number | 10 bits: Sequence number |

Java code to generate a unique ID:

/**
* Custom ID generator
* ID generation rule: ID is 64 bits long
*
* | 41 bits: Timestamp (milliseconds) | 3 bits: Region (data center) | 10 bits: Machine number | 10 bits: Sequence number |
*/
public class GameUUID{
// Base time
private long twepoch = 1288834974657L; //Thu, 04 Nov 2010 01:42:54 GMT
// Number of bits for region flag
private final static long regionIdBits = 3L;
// Number of bits for machine identifier
private final static long workerIdBits = 10L;
// Number of bits for sequence ID
private final static long sequenceBits = 10L;
// Maximum value of region flag ID
private final static long maxRegionId = -1L ^ (-1L << regionIdBits);
// Maximum value of machine ID
private final static long maxWorkerId = -1L ^ (-1L << workerIdBits);
// Maximum value of sequence ID
private final static long sequenceMask = -1L ^ (-1L << sequenceBits);
// Machine ID shifted left by 10 bits
private final static long workerIdShift = sequenceBits;
// Business ID shifted left by 20 bits
private final static long regionIdShift = sequenceBits + workerIdBits;
// Time milliseconds shifted left by 23 bits
private final static long timestampLeftShift = sequenceBits + workerIdBits + regionIdBits;
private static long lastTimestamp = -1L;
private long sequence = 0L;
private final long workerId;
private final long regionId;
public GameUUID(long workerId, long regionId) {
// إذا كان يخرج عن النطاق يلقي استثناء
إذا (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException("worker Id can't be greater than %d or less than 0");
}
إذا (regionId > maxRegionId || regionId < 0) {
throw new IllegalArgumentException("datacenter Id can't be greater than %d or less than 0");
}
this.workerId = workerId;
this.regionId = regionId;
}
public GameUUID(long workerId) {
// إذا كان يخرج عن النطاق يلقي استثناء
إذا (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException("worker Id can't be greater than %d or less than 0");
}
this.workerId = workerId;
this.regionId = 0;
}
public long generate() {
return this.nextId(false, 0);
}
/**
* يولد الكود الفعلي
*
* @param isPadding
* @param busId
* @return
*/
private synchronized long nextId(boolean isPadding, long busId) {
long timestamp = timeGen();
long paddingnum = regionId;
إذا (isPadding) {
paddingnum = busId;
}
إذا (timestamp < lastTimestamp) {
try {
throw new Exception("Clock moved backwards. Refusing to generate id for " + (lastTimestamp - timestamp) + " milliseconds");
}
e.printStackTrace();
}
}
// إذا كان الوقت المولد الماضي نفسه كما هو الوقت الحالي، في نفس الميليسيوند
if (lastTimestamp == timestamp) {
// زيادة sequence، لأن sequence يحتوي فقط على 10 bits، لذا يتم جمعه مع sequenceMask، لإزالة المراكز العليا
sequence = (sequence + 1) & sequenceMask;
// التحقق من الانهيار، أي إذا كان هناك أكثر من 1024 في كل ميليسيوند، عند الوصول إلى 1024، يتم جمعه مع sequenceMask، sequence يساوي 0
if (sequence == 0) {
// الإنتظار التدويري حتى يصل إلى الميليسيوند التالي
timestamp = tailNextMillis(lastTimestamp);
}
}
// إذا كان الوقت المولد الماضي مختلفًا عن الوقت الحالي، يتم إعادة تعيين sequence، أي أن sequence تبدأ من الصفر في بداية الميليسيوند التالي
// لضمان أن يكون الرقم التافهي عشوائيًا بشكل أكبر، يتم تعيين الرقم التافهي الأخير إلى عدد عشوائي
sequence = new SecureRandom().nextInt(10);
}
lastTimestamp = timestamp;
return ((timestamp - twepoch) << timestampLeftShift) | (paddingnum << regionIdShift) | (workerId << workerIdShift) | sequence;
}
// حماية من توليد الوقت الذي هو أصغر من الوقت السابق (بسبب NTP التبديل وما إلى ذلك)، الحفاظ على الاتجاه التدريجي.
private long tailNextMillis(final long lastTimestamp) {
long timestamp = this.timeGen();
while (timestamp <= lastTimestamp) {
timestamp = this.timeGen();
}
return timestamp;
}
// الحصول على الوقت الحالي
protected long timeGen() {
return System.currentTimeMillis();
}
}

عند استخدام هذا الأسلوب المخصص، يجب الانتباه إلى النقاط التالية:

للحفاظ على الاتجاه النمو، يجب تجنب أن تكون بعض خوادم الوقت مبكرة، وبعض خوادم الوقت متأخرة، يجب التحكم في وقت جميع الخوادم، ويجب تجنب رجوع وقت خوادم NTP إلى وقت الخوادم; عند العبور من ميللي ثانية إلى ميللي ثانية، يتم إعادة تعيين الرقم التسلسلي دائمًا إلى 0، مما يؤدي إلى زيادة عدد IDs المتسلسلين 0، مما يؤدي إلى عدم توزيع متساوٍ لـ IDs بعد أخذ النمط، لذا لا يتم إعادة تعيين الرقم التسلسلي دائمًا إلى 0، بل يتم إعادة تعيينه إلى عدد عشوائي بين 0 و9.

يمكننا اختيار الطريقة المذكورة أعلاه بناءً على احتياجاتنا. في تطوير خادم اللعبة، يمكننا اختيار النوع الخاص بلعبتنا، مثل اللعبة المحمولة، يمكن استخدام طريقة redis بسيطة، بسيطة وليست معقدة، ولا توجد أخطاء، لأن كمية IDs الجديدة المبنية في الخادم الفردي ليست كبيرة جدًا، مما يكفي احتياجاتنا. بينما الخادم العالمي الكبير للعبة، فهو يعتمد على التوزيع بشكل رئيسي، لذا يمكننا استخدام طريقة snowflake، والكود المذكور أعلاه هو مجرد مثال، يجب أن يتم تخصيصه بناءً على احتياجاتك الخاصة، لذا هناك كمية إضافية من العمل التنفيذي، وينبغي الانتباه إلى النقاط المذكورة أعلاه.

ما ذكرته أعلاه هو مجموعة من الطرق التي يمكننا تنفيذها بناءً على لغة Java لإنشاء ID العالمية الفريدة للخادم اللعبة، آمل أن تكون مفيدًا لكم، إذا كان لديكم أي أسئلة، فلا تترددوا في ترك تعليق، وسأقوم بالرد على أسئلتكم في أقرب وقت. وأشكركم أيضًا على دعمكم لموقع呐喊 لتعليم البرمجة!

بيان: محتويات هذا المقال تم جمعها من الإنترنت، ملكية المادة تخص صاحب الحقوق، المحتوى تم إضافته من قبل مستخدمي الإنترنت بشكل تلقائي، هذا الموقع لا يمتلك حقوق الملكية، لم يتم تعديل المحتوى بشكل يدوي، ولا يتحمل أي مسؤولية قانونية. إذا اكتشفت أي محتوى يشتبه في انتهاك حقوق النسخ، فلا تتردد في إرسال بريد إلكتروني إلى: notice#oldtoolbag.com (عند إرسال البريد الإلكتروني، يرجى استبدال # بـ @) للإبلاغ، وقدم الدليل على الدليل، إذا تم التحقق من ذلك، سيتم حذف المحتوى المزعوم فورًا.

من المحتمل أن تحبها