Die eindeutige System-ID ist ein Problem, auf das wir beim Entwerfen eines Systems häufig stoßen, und wir haben oft mit diesem Problem zu kämpfen. Es gibt viele Möglichkeiten, IDs zu generieren und sich an unterschiedliche Szenarien, Bedürfnisse und Leistungsanforderungen anzupassen. Daher verfügen einige komplexere Systeme über mehrere Strategien zur ID-Generierung. Hier sind einige gängige Strategien zur ID-Generierung.
1. Selbstvergrößernde Sequenz oder Feld der Datenbank
Die gebräuchlichste Methode. Bei Verwendung der Datenbank ist die gesamte Datenbank eindeutig.
Vorteile:
Einfacher, praktischer Code, akzeptable Leistung.
Numerische IDs werden natürlich sortiert, was beim Paginieren oder bei Ergebnissen, die sortiert werden müssen, hilfreich ist.
Nachteile:
Unterschiedliche Datenbanksyntax und -implementierung sind unterschiedlich, wenn eine Datenbankmigration durchgeführt wird oder wenn mehrere Datenbankversionen unterstützt werden Muss bearbeitet werden.
Im Falle einer einzelnen Datenbank oder einer Lese-Schreib-Trennung oder einem Master und mehreren Slaves, dort Es kann nur eine Masterdatenbank generiert werden. Es besteht die Gefahr eines Single Point of Failure.
Eine Erweiterung ist schwierig, wenn die Leistung den Anforderungen nicht gerecht wird.
Wenn Sie auf mehrere Systeme stoßen, die zusammengeführt werden müssen oder eine Datenmigration involviert ist, wird es ruhig schmerzhaft.
Beim Teilen von Tabellen und Datenbanken wird es Probleme geben.
Optimierungsplan:
Für einen einzelnen Punkt in der Hauptbibliothek, wenn jeweils mehrere Master-Bibliotheken vorhanden sind Master Die von der Bibliothek festgelegte Startnummer ist unterschiedlich, die Schrittgröße ist jedoch dieselbe und kann die Anzahl der Master sein. Beispiel: Master1 generiert 1, 4, 7, 10, Master2 generiert 2,5,8,11, Master3 generiert 3,6,9,12. Dies kann effektiv eindeutige IDs im Cluster generieren und auch die Belastung durch Datenbankoperationen zur ID-Generierung erheblich reduzieren.
2. UUID-gängige Methoden.
Es kann mithilfe einer Datenbank oder eines Programms generiert werden und ist im Allgemeinen weltweit einzigartig.
Vorteile:
Einfacher und praktischer Code.
Die Leistung bei der ID-Generierung ist sehr gut und es gibt grundsätzlich keine Leistungsprobleme.
Weltweit einzigartig, bei Datenmigration, Systemdatenzusammenführung oder Datenbankänderungen, Sie können es problemlos ertragen.
Nachteile:
Es gibt keine Sortierung und es kann nicht garantiert werden, dass der Trend zunimmt.
UUID wird häufig mithilfe von Zeichenfolgen gespeichert und die Abfrageeffizienz ist relativ gering.
Der Speicherplatz ist relativ groß, Sie müssen den Speicher berücksichtigen Menge.
Große zu übertragende Datenmenge
ist nicht lesbar.
3. Redis generiert ID
Wenn die Leistung der Verwendung der Datenbank zum Generieren von IDs nicht ausreicht, können wir versuchen, Redis zu verwenden ID generieren. Dies beruht hauptsächlich darauf, dass Redis Single-Threaded ist, sodass es auch zum Generieren global eindeutiger IDs verwendet werden kann. Dies kann mit den atomaren Operationen INCR und INCRBY von Redis erreicht werden.
Sie können den Redis-Cluster verwenden, um einen höheren Durchsatz zu erzielen. Angenommen, es gibt 5 Redis in einem Cluster. Die Werte jedes Redis können auf 1, 2, 3, 4, 5 initialisiert werden, und dann beträgt die Schrittgröße alle 5. Die von jedem Redis generierten IDs sind:
A: 1,6,11,16,21 B: 2,7,12,17,22 C: 3,8,13,18,23 D: 4 , 9,14,19,24 E: 5,10,15,20,25
Dies kann von der Maschine bestimmt werden, auf die es geladen wird. Es wird in Zukunft schwierig sein, es zu ändern. Grundsätzlich können jedoch 3-5 Server die Anforderungen des Servers erfüllen und alle können unterschiedliche IDs erhalten. Die Schrittgröße und der Anfangswert müssen jedoch im Voraus angegeben werden. Die Verwendung eines Redis-Clusters kann auch das Problem des Single Point of Failure lösen.
Darüber hinaus ist es besser geeignet, Redis zu verwenden, um eine tägliche Seriennummer beginnend bei 0 zu generieren. Beispiel: Bestellnummer = Datum + sich selbst erhöhende Zahl an diesem Tag. Sie können jeden Tag einen Schlüssel in Redis generieren und INCR zur Akkumulation verwenden.
Vorteile:
Hängt nicht von der Datenbank ab, ist flexibel und bequem und hat eine bessere Leistung als die Datenbank.
Numerische IDs werden natürlich sortiert, was beim Blättern oder bei Ergebnissen, die sortiert werden müssen, hilfreich ist.
Nachteile:
Wenn kein Redis im System vorhanden ist, müssen neue Komponenten eingeführt werden, was die Systemkomplexität.
Der Arbeitsaufwand für Codierung und Konfiguration ist relativ groß.
4. Der Snowflake-Algorithmus von Twitter
Snowflake ist der Open-Source-Algorithmus zur verteilten ID-Generierung, und das Ergebnis ist eine lange ID. Die Kernidee besteht darin, 41 Bit als Anzahl der Millisekunden, 10 Bit als Maschinen-ID (5 Bit sind das Rechenzentrum, 5 Bit die Maschinen-ID) und 12 Bit als Seriennummer innerhalb von Millisekunden zu verwenden (was bedeutet, dass jeder Knoten dies tun kann). generiert 4096 IDs) und am Ende steht ein Vorzeichenbit, das immer 0 ist. Den spezifischen Implementierungscode finden Sie unter: https://github.com/twitter/snowflake
public class IdWorker { // ==============================Fields=========================================== /** 开始时间截 (2015-01-01) */ private final long twepoch = 1420041600000L; /** 机器id所占的位数 */ private final long workerIdBits = 5L; /** 数据标识id所占的位数 */ private final long datacenterIdBits = 5L; /** 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数) */ private final long maxWorkerId = -1L ^ (-1L << workerIdBits); /** 支持的最大数据标识id,结果是31 */ private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); /** 序列在id中占的位数 */ private final long sequenceBits = 12L; /** 机器ID向左移12位 */ private final long workerIdShift = sequenceBits; /** 数据标识id向左移17位(12+5) */ private final long datacenterIdShift = sequenceBits + workerIdBits; /** 时间截向左移22位(5+5+12) */ private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; /** 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095) */ private final long sequenceMask = -1L ^ (-1L << sequenceBits); /** 工作机器ID(0~31) */ private long workerId; /** 数据中心ID(0~31) */ private long datacenterId; /** 毫秒内序列(0~4095) */ private long sequence = 0L; /** 上次生成ID的时间截 */ private long lastTimestamp = -1L; //==============================Constructors===================================== /** * 构造函数 * @param workerId 工作ID (0~31) * @param datacenterId 数据中心ID (0~31) */ public IdWorker(long workerId, long datacenterId) { if (workerId > maxWorkerId || workerId < 0) { throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId)); } if (datacenterId > maxDatacenterId || datacenterId < 0) { throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId)); } this.workerId = workerId; this.datacenterId = datacenterId; } // ==============================Methods========================================== /** * 获得下一个ID (该方法是线程安全的) * @return SnowflakeId */ public synchronized long nextId() { long timestamp = timeGen(); //如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常 if (timestamp < lastTimestamp) { throw new RuntimeException( String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp)); } //如果是同一时间生成的,则进行毫秒内序列 if (lastTimestamp == timestamp) { sequence = (sequence + 1) & sequenceMask; //毫秒内序列溢出 if (sequence == 0) { //阻塞到下一个毫秒,获得新的时间戳 timestamp = tilNextMillis(lastTimestamp); } } //时间戳改变,毫秒内序列重置 else { sequence = 0L; } //上次生成ID的时间截 lastTimestamp = timestamp; //移位并通过或运算拼到一起组成64位的ID return ((timestamp - twepoch) << timestampLeftShift) // | (datacenterId << datacenterIdShift) // | (workerId << workerIdShift) // | sequence; } /** * 阻塞到下一个毫秒,直到获得新的时间戳 * @param lastTimestamp 上次生成ID的时间截 * @return 当前时间戳 */ protected long tilNextMillis(long lastTimestamp) { long timestamp = timeGen(); while (timestamp <= lastTimestamp) { timestamp = timeGen(); } return timestamp; } /** * 返回以毫秒为单位的当前时间 * @return 当前时间(毫秒) */ protected long timeGen() { return System.currentTimeMillis(); } //==============================Test============================================= /** 测试 */ public static void main(String[] args) { IdWorker idWorker = new IdWorker(0, 0); for (int i = 0; i < 1000; i++) { long id = idWorker.nextId(); System.out.println(Long.toBinaryString(id)); System.out.println(id); } }}
Der Schneeflocken-Algorithmus kann entsprechend den Anforderungen Ihres eigenen Projekts geändert werden. Schätzen Sie beispielsweise die Anzahl zukünftiger Rechenzentren, die Anzahl der Maschinen in jedem Rechenzentrum und die Anzahl möglicher Parallelitäten in einer einheitlichen Millisekunde, um die Anzahl der im Algorithmus erforderlichen Bits anzupassen.
Vorteile:
Hängt nicht von der Datenbank ab, ist flexibel und bequem und hat eine bessere Leistung als die Datenbank.
ID erhöht sich auf einer einzelnen Maschine im Laufe der Zeit.
Nachteile:
ist inkrementell auf einer einzelnen Maschine, aber da es sich um eine verteilte Umgebung handelt, muss jede Maschine die Uhren auf der Uhr können nicht vollständig synchronisiert werden, und manchmal kann es Situationen geben, in denen das globale Inkrement nicht erreicht wird.
5. Verwenden Sie zookeeper, um eine eindeutige ID zu generieren
zookeeper generiert Seriennummern hauptsächlich über seine Znode-Datenversion, die 32-Bit- und 64-Bit-Datenversionsnummern generieren kann . Kunden Der Kunde kann diese Versionsnummer als eindeutige Seriennummer verwenden.
Zookeeper wird selten zum Generieren eindeutiger IDs verwendet. Hauptsächlich, weil es auf Zookeeper basiert und die API in mehreren Schritten aufruft. Wenn die Konkurrenz groß ist, müssen Sie die Verwendung verteilter Sperren in Betracht ziehen. Daher ist die Leistung in einer verteilten Umgebung mit hoher Parallelität nicht ideal.
6. Die ObjectId von MongoDB
Die ObjectId von MongoDB ähnelt dem Snowflake-Algorithmus. Es ist auf ein geringes Gewicht ausgelegt und kann problemlos von verschiedenen Maschinen mit derselben weltweit einzigartigen Methode erzeugt werden. MongoDB wurde von Anfang an als verteilte Datenbank konzipiert und die Handhabung mehrerer Knoten ist eine Grundvoraussetzung. Dies erleichtert die Generierung in einer Sharding-Umgebung erheblich. Das Format ist wie folgt: [src/main/resources/objectId.png] Schreiben Sie die Bildbeschreibung hier:
Die ersten 4 Bytes sind der Zeitstempel ab der Standardepoche , Einheit ist Sekunden. Der Zeitstempel sorgt in Kombination mit den folgenden 5 Bytes für Eindeutigkeit der zweiten Ebene. Da der Zeitstempel an erster Stelle steht, bedeutet dies, dass die ObjectIds grob in der Reihenfolge sortiert werden, in der sie eingefügt wurden. Dies ist beispielsweise nützlich, um es als Index zur Verbesserung der Effizienz zu verwenden. Diese 4 Bytes geben auch den Zeitpunkt an, zu dem das Dokument erstellt wurde. Die meisten Clientbibliotheken stellen eine Methode bereit, um diese Informationen aus der ObjectId abzurufen. Die nächsten 3 Bytes sind die eindeutige Kennung des Hosts. Normalerweise ein Hash des Hostnamens der Maschine. Dadurch wird sichergestellt, dass verschiedene Hosts konfliktfrei unterschiedliche ObjectIds generieren. Um sicherzustellen, dass die von mehreren gleichzeitigen Prozessen auf demselben Computer generierte ObjectId eindeutig ist, stammen die nächsten beiden Bytes von der Prozesskennung (PID), die die ObjectId generiert hat. Die ersten 9 Bytes stellen sicher, dass die ObjectId, die von verschiedenen Prozessen auf verschiedenen Maschinen in derselben Sekunde generiert wird, eindeutig ist. Die letzten 3 Bytes sind ein automatisch steigender Zähler, um sicherzustellen, dass die vom selben Prozess in derselben Sekunde generierte ObjectId auch unterschiedlich ist. Jeder Prozess darf in derselben Sekunde bis zu 2563 (16.777.216) verschiedene ObjectIds haben.
Verwandte Empfehlungen:
Beispiel für die Entwicklung eines PHP-Managementsystems für Pressemitteilungen
Tutorial für die PHP-Entwicklung eines einfachen Pressemitteilungssystems
Das obige ist der detaillierte Inhalt vonZusammenfassung einzigartiger ID-Generierungslösungen für verteilte Systeme. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!