Encountered several scenarios that need to produce unique keys
1: Warehousing system, all kinds of order numbers in the warehouse need to be unique and have certain business significance
Requirements: AAAA20151212000000001 AAAA is the in-cabin service code, the middle 8 digits are the corresponding year, month, and day, and the last 8 digits are the unique key guarantee.
2: E-commerce order system, the order number needs to be unique
Requirements: 28710080047686 The last four digits are the buyer's ID (for sub-database and sub-table), and the front data is the unique key guarantee bit.
3: The unique key requirements for registered user ID and comment ID in the chat app system.
Requirement: etx32s user ID unique value
Among the three requirements, a key value that can guarantee the uniqueness of the serial number is required. In distributed systems, there are many solutions to ensure unique key values. For example: simple and rude DB self-increment sequence, MD5 (basically unique), and in scenario 1, the distributed lock made by tair (Ali middleware) used at that time guarantees self-increment and uniqueness. This time I introduce a design scheme of sequence generation middleware products.
Overall program:
ps:
Overall plan idea:
The application cluster obtains an id each time and caches it locally. Every time you use it, get it locally. When the local id segment is used up, make another remote call to update.
On the server side, DB is the master-slave configuration, and MHA is used for abnormal master-slave switching.
Capacity Analysis:
Cluster 10wqps, 500 servers. The single-machine id segment is set to 1w length, the request pressure on the server side is less than 1qps, the server side has no pressure, and most of them are obtained locally, without rpc consumption.
example:
1: Client initialization
//Initialize the client Util
public static IdGenerator instance(String... password) {
IdGenerator ig = generators.get(key);
if (ig == null) {
synchronized(generators) {
ig = generators.get(key);
if (ig == null) {
ig = new IdRangeIncrementGenerator(tmpPassword, idRangeDispatcher);
generators.putIfAbsent(key, ig);
}
}
}
return ig;
}
//Initialize client serialization segment and local Queue
public IdRangeIncrementGenerator(final String schema, final String table, final String password,
IdRangeDispatcher dispatcher) throws NoSuchGeneratorException {
super(dispatcher);
checkArgument(isNotBlank(password), "The password is blank");
this.password = password;
//Get idRange asynchronously
backThread = new Thread(new Runnable() {
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
try {
IdRange idRange = retryNext(password);//Call the server and go to the DB to get the Id
backQueue.put(new IdRangeRound(idRange));
} catch (InterruptedException ie) {
logger.error("interrupted.");
Thread.currentThread().interrupt();
break;
} catch (Throwable e) {
logger.error("backThread retryNext() error", e);
}
}
}
});
backThread.setName("Raptor-IdGenerator-backThread");
backThread.setDaemon(true);
backThread.start();
IdRangeRound idRange = reload(idRangeRound);
String msg = "Can't get idRange by password[" + password + "]";
checkNotNull (idRange, msg);
}
2 The client obtains the Id segment and uses it
//Get directly from the local method
@Override
public long next(int num) throws NoMoreIdException {
checkArgument(num > 0, "The num[" + num + "] must be > 0 ");
long[] ids = new long[num];
for (int i = 0; i < num; i++) {
long id;
IdRangeRound is = idRangeRound;
while ((id = ir.next(1)) == -1) {
//When the local id segment is not enough, re-remotely obtain and update the id segment
go = reload(go);
}
ids[i] = id;
}
return ids;
}
/ / Block thread update, re-acquire the Id side
private synchronized IdRangeRound reload(IdRangeRound ir) {
if (ir != idRangeRound) {
return idRangeRound;
}
try {
return idRangeRound = backQueue.take();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
3 Server-side DB disaster recovery solution: MHA