Redisによる分散シングルナンバーと分散ID(カスタムルール生成)
背景
ビジネスの文脈によっては、異なる注文番号を区別するために接頭辞が必要になることがあります。分散アーキテクチャで注文番号をカスタマイズし、かつ一意性を確保するにはどうすればよいでしょうか。
参考:分散IDはこんな使い方もできます
Redis の実装
Redisのコマンド操作はすべてシングルスレッドで、incrやincrebyといった自己増強型のアトミックコマンドを提供しているので、生成されるIDは確実に一意的に並べられるようになっています。
長所 データベースに依存しない、柔軟、便利、データベースを凌駕する。数値IDは自然にソートされるので、ページングやソートが必要な結果には有効である。
短所:Redisを導入していない場合、新たなコンポーネントも導入する必要があり、システムが複雑になる、コーディングや設定作業が増える。
シングルノードの性能ボトルネックを考えると、Redisクラスターを利用することでより高いスループットを得ることができます。
また、Redisクラスタを使用することで、単一障害点の問題を解決することができます。
コード例
定数クラスの作成
/**
* Single number generation constants
*
* @author mq
*/
public class FormNoConstants {
/**
* Single number cache Key prefix
*/
public static final String SERIAL_CACHE_PREFIX = "FORM_NO_CACHE_";
/**
* Single number stream number yyMMdd prefix
*/
public static final String SERIAL_YYMMDD_PREFIX = "yyMMdd";
/**
* single running number yyyyMMdd prefix
*/
public static final String SERIAL_YYYYMMDD_PREFIX = "yyyyMMdd";
/**
* default cache days
*/
public static final int DEFAULT_CACHE_DAYS = 7;
}
シングルナンバー生成列挙
注:拡張や再利用を容易にするために、列挙型のメソッドを使用し、列挙値をカスタマイズして異なる単一番号を生成することができます。
/**
* Single number generation type enumeration
*
* @author mq
* Note: The random number is located after the running number, running number using redis count data, each day is a new key, the length is insufficient to automatically fill 0
* <p>
* generation rules = fixed prefix + day date string + running number (redis self-incrementing, insufficient length to fill 0) + random number
*/
public enum FormNoTypeEnum {
/**
* Payable order number.
* Fixed prefix: YF
* Time format: yyyyMMdd
* Flow number length: 7 (when a single day documents more can be increased according to business appropriate flow number length)
* Random number length: 3
* Total length: 20
*/
YF_ORDER("YF", FormNoConstants.SERIAL_YYYYMMDD_PREFIX, 7, 3, 20),
/**
* Payment order number.
* Fixed prefix: FK
* Time format: yyyyMMdd
* Flow number length: 7
* Random number length: 3
* Total length: 20
*/
FK_ORDER("FK", FormNoConstants.SERIAL_YYYYMMDD_PREFIX, 7, 3, 20),
/**
* Test order number.
* Fixed prefix: ""
* Time format: yyyyMMdd
* Flow number length: 10
* Random number length: 0
* Total length: 20
*/
TEST_ORDER("te", FormNoConstants.SERIAL_YYYYMMDD_PREFIX, 10, 0, 20),
;
/**
* Single number prefix
* Filled with "" when empty;
*/
private String prefix;
/**
* Time format expressions
* Example: yyyyMMdd
*/
private String datePattern;
/**
* The length of the stream number
*/
private Integer serialLength;
/**
* The length of the random number
*/
private Integer randomLength;
/**
* Total length
*/
private Integer totalLength;
FormNoTypeEnum(String prefix, String datePattern, Integer serialLength, Integer randomLength, Integer totalLength) {
this.prefix = prefix;
this.datePattern = datePattern;
this.serialLength = serialLength;
this.randomLength = randomLength;
this.totalLength = totalLength;
}
//Omit the get method
}
シングルナンバー生成ツールクラス
/**
* Single number generation tool class
*
* @author mq
*/
public class FormNoSerialUtil {
/**
* Generate a single number prefix
*/
public static String getFormNoPrefix(FormNoTypeEnum formNoTypeEnum) {
// format the time
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(formNoTypeEnum.getDatePattern());
StringBuffer sb = new StringBuffer();
sb.append(formNoTypeEnum.getPrefix());
sb.append(formatter.format(LocalDateTime.now()));
return sb.toString();
}
/**
* Construct the running number cache Key
*
* @param serialPrefix stream number prefix
* @return stream number cache Key
*/
public static String getCacheKey(String serialPrefix) {
return FormNoConstants.SERIAL_CACHE_PREFIX.concat(serialPrefix);
}
/**
* Complete the running number
*
* @param serialPrefix single number prefix
* @param incrementalSerial The current day's self-adding serial number
* @author mengqiang
* @date 2019/1/1
*/
public static String completionSerial(String serialPrefix, Long incrementalSerial,
FormNoTypeEnum formNoTypeEnum) {
StringBuffer sb = new StringBuffer(serialPrefix);
// the length to be filled with zeros = the length of the serial number - the length of the day's incremental count
int length = formNoTypeEnum.getSerialLength() - String.valueOf(incrementalSerial).length();
//fill zero
for (int i = 0; i < length; i++) {
sb.append("0");
}
//redis self-incrementing number for the day
sb.append(incrementalSerial);
return sb.toString();
}
/**
* Completes the random number
*
* @param serialWithPrefix current single number
* @param formNoTypeEnum Single number generation enum
* @author mengqiang
* @date 2019/1/1
*/
public static String completionRandom(String serialWithPrefix, FormNoTypeEnum formNoTypeEnum) {
StringBuffer sb = new StringBuffer(serialWithPrefix);
//the length of the random number
int length = formNoTypeEnum.getRandomLength();
if (length > 0) {
Random random = new Random();
for (int i = 0; i < length; i++) {
//complete random numbers within ten
sb.append(random.nextInt(10));
}
}
return sb.toString();
}
}
単一番号生成インターフェース
/**
* Single number generation interface
*
* @author mq
*/
public interface FormNoGenerateService {
/**
* Generate document number based on document number type
*
* @param formNoTypeEnum document number type
* @author mengqiang
* @date 2019/1/1
*/
String generateFormNo(FormNoTypeEnum formNoTypeEnum);
}
単一番号生成インターフェース実装
/**
* Single number generation interface implementation
*
* @author mengqiang
* @version FormNoGenerateServiceImpl.java, v 1.0 2019-01-01 18:10
*/
@Service
public class FormNoGenerateServiceImpl implements FormNoGenerateService {
/**
* redis service
* demo project does not add redis-related, if necessary, please refer to the redis blog
*/
@Autowired
private RedisCache redisCache;
/*
* Generate a document number based on the document number type
*
* @param formNoTypeEnum Document number type
* @author mengqiang
* @date 2019/1/1
*/
@Override
public String generateFormNo(FormNoTypeEnum formNoTypeEnum) {
//Get the single number prefix
//format Fixed prefix + time prefix Example : YF20190101
String formNoPrefix = FormNoSerialUtil.getFormNoPrefix(formNoTypeEnum);
//Get the cache key
String cacheKey = FormNoSerialUtil.getCacheKey(formNoPrefix);
//Get the current day's self-incremental number
Long incrementalSerial = redisCache.incr(cacheKey);
//set expiration time 7 days
redisCache.expire(cacheKey, FormNoConstants.DEFAULT_CACHE_DAYS, TimeUnit.DAYS);
//combine single number and complete the serial number
String serialWithPrefix = FormNoSerialUtil
.completionSerial(formNoPrefix, incrementalSerial, formNoTypeEnum);
//completion of random number
return FormNoSerialUtil.completionRandom(serialWithPrefix, formNoTypeEnum);
}
}
テストを使用する
redisのスクリーンショット
概要
上記は最もエレガントな方法ではなく、ユニバーサルなサービスにするために、ジャーパッケージ方式にするのが良いだろう
Redisベースの分散シングル・分散ID(カスタムルール生成)については、この記事がすべてです。Redisベースの分散シングルと分散ID(カスタムルール生成)については、Script Houseの過去記事を検索するか、以下の記事を引き続きご覧ください。
関連
-
redis の RedissonLock が待ちロックを実装する方法
-
Redisによる分散ロック(setnx, getset, incr)の実装とタイムアウトの扱い方
-
Redis 3つのクラスターモードの説明
-
インタビューFAQです。Redisキャッシュとデータベース間のデータ整合性を確保する方法
-
Redis 高効率化の理由とデータ構造の解析
-
redisを使ってnearly peopleの機能を実装する
-
Redisトランザクション処理の使用方法
-
シングルスレッドのredisがなぜ速いのかの紹介
-
SpringBootがRedisの分散ロックを利用して並行処理の問題を解決することについて
-
Redisシングルスレッディングの正しい理解
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
JAVAでRedisの5つのデータ構造を利用する方法
-
redisクラスタ構築プロセス (非常に詳細、初心者向け)
-
Redisで緯度・経度座標データを簡単に扱う方法
-
SpringBootのRedis連携のアイデア解説
-
SpringBootプロジェクトにおけるRedis。包括的なアプリケーション
-
Redisは携帯電話の認証コードを送信する機能を模倣している
-
Redisの高同期スパイクを防ぐために、ソースコードソリューションを売られすぎ
-
Redis の例外と使用法のまとめ
-
インストール後、Redis-cliが動作しない(redis-cli: コマンドが見つからない)。
-
RedisTemplateでRedisを操作する、この記事で十分です(a)