自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

簡(jiǎn)單實(shí)用!利用Redis輕松實(shí)現(xiàn)高并發(fā)全局ID生成器

數(shù)據(jù)庫(kù) Redis
今天我就給大家?guī)?lái)一個(gè)神器級(jí)的解決方案——Redis 分布式 ID 生成器!配合 SpringBoot3.0,讓你的 ID 生成變得既簡(jiǎn)單又高效。

我相信你會(huì)經(jīng)常遇到要生成唯一 ID 的場(chǎng)景,比如標(biāo)識(shí)每次請(qǐng)求、生成一個(gè)訂單編號(hào)、創(chuàng)建用戶需要?jiǎng)?chuàng)建一個(gè)用戶 ID。

謝霸戈:這還不簡(jiǎn)單,用 UUID 不就行了。

UUID 確實(shí)是個(gè)好東西,生成的 ID 全球唯一,但是有兩個(gè)致命缺陷。

  • 不是遞增的。MySQL 中索引的數(shù)據(jù)結(jié)構(gòu)是 B+Tree,這種數(shù)據(jù)結(jié)構(gòu)的特點(diǎn)是索引樹(shù)上的節(jié)點(diǎn)的數(shù)據(jù)是有序的,而如果使用 UUID 作為主鍵,那么每次插入數(shù)據(jù)時(shí),因?yàn)闊o(wú)法保證每次產(chǎn)生的 UUID 有序,所以就會(huì)出現(xiàn)新的 UUID 需要插入到索引樹(shù)的中間去,這樣可能會(huì)頻繁地導(dǎo)致頁(yè)分裂,使性能下降。
  • 太占用內(nèi)存。每個(gè) UUID 由 36 個(gè)字符組成,在字符串進(jìn)行比較時(shí),需要從前往后比較,字符串越長(zhǎng),性能越差。另外字符串越長(zhǎng),占用的內(nèi)存越大,由于頁(yè)的大小是固定的,這樣一個(gè)頁(yè)上能存放的關(guān)鍵字?jǐn)?shù)量就會(huì)越少,這樣最終就會(huì)導(dǎo)致索引樹(shù)的高度越大,在索引搜索的時(shí)候,發(fā)生的磁盤(pán) IO 次數(shù)越多,性能越差。

謝霸戈:那咋辦呢?

別急,今天我就給大家?guī)?lái)一個(gè)神器級(jí)的解決方案——Redis 分布式 ID 生成器!配合 SpringBoot3.0,讓你的 ID 生成變得既簡(jiǎn)單又高效。

分布式 ID 要滿足什么要求

在進(jìn)入正文前,先介紹下分布式 ID 應(yīng)該滿足哪些特性。

分布式 ID 生成器需要滿足以下特性。

  1. 有序性之單調(diào)遞增,想要分而治之、二分法查找就必須實(shí)現(xiàn)。另外,MySQL 是你們用的最多的數(shù)據(jù)庫(kù),B+ 樹(shù)為了維護(hù) ID 的有序性,就會(huì)頻繁的在索引的中間位置插入而挪動(dòng)后面節(jié)點(diǎn)的位置,甚至導(dǎo)致頻繁的頁(yè)分裂,這對(duì)于性能的影響是極大的。
  2. 全局唯一性,ID 不唯一就會(huì)出現(xiàn)主鍵沖突。
  3. 高性能,生成 ID 是高頻操作,如果性能緩慢,系統(tǒng)的整體性能都會(huì)受到限制。
  4. 高可用,也就是在給定的時(shí)間間隔內(nèi),一個(gè)系統(tǒng)總的可用時(shí)間占的比例。
  5. 存儲(chǔ)空間小,用 MySQL 的 InnoDB B+樹(shù)來(lái)說(shuō),普通索引(非聚集索引)會(huì)存儲(chǔ)主鍵值,主鍵越大,每個(gè) Page 頁(yè)可以存儲(chǔ)的數(shù)據(jù)就越少,訪問(wèn)磁盤(pán) I/O 的次數(shù)就會(huì)增加。

Redis String 實(shí)現(xiàn)分布式 ID

Redis 集群能保證高可用和高性能,為了節(jié)省內(nèi)存,ID 可以使用數(shù)字的形式,并且通過(guò)遞增的方式來(lái)創(chuàng)建新的 ID。

防止重啟數(shù)據(jù)丟失,你還需要把 Redis AOF 持久化開(kāi)啟。

MySQL:“開(kāi)啟 AOF 持久,為了性能設(shè)置成 everysec 策略還是有可能丟失一秒的數(shù)據(jù),所以你還可以使用一個(gè)異步機(jī)制將生成的最大 ID 持久化到一個(gè) MySQL?!?/p>

好主意,在生成 ID 之后發(fā)送一條消息到 MQ 消息隊(duì)列中,把值持久化到 MySQL 中。

我們可以使用 Redis String 數(shù)據(jù)類(lèi)型來(lái)實(shí)現(xiàn),key 用于區(qū)分不同業(yè)務(wù)場(chǎng)景的 ID 生成器,value 存儲(chǔ) ID。

String 數(shù)據(jù)類(lèi)型提供了 INCR 指令,它能把 key 中存儲(chǔ)的數(shù)字加 1 并返回客戶端。如果 key 不存在,那么 key 的 value 先被初始化成 0,再執(zhí)行加 1 操作并返回給客戶端。

Redis,作為一個(gè)高性能的內(nèi)存數(shù)據(jù)庫(kù),天生就適合處理高并發(fā)的場(chǎng)景。它的“單線程”模型更是讓它在處理 ID 生成時(shí)如魚(yú)得水。

Redis 的操作是原子性的,這就意味著在整個(gè)過(guò)程中,不會(huì)有任何的并發(fā)問(wèn)題出現(xiàn),從而確保了 ID 的唯一性。

設(shè)計(jì)思路

設(shè)計(jì)思路如下圖所示。

圖 2-4

  1. 假設(shè)訂單 ID 生成器的 key 是“counter:order”,當(dāng)應(yīng)用服務(wù)啟動(dòng)的時(shí)候先從數(shù)據(jù)庫(kù)中查詢出最大值 M。執(zhí)行 EXISTS counter:order 判斷是否存在 key。
  • Redis 中不存在 key “counter:order”,執(zhí)行 SET counter:order M 將 M 值作寫(xiě)入 Redis。
  • Redis 中存在 key “counter:order”,值為 K,那么就比較 M 和 K 的值,執(zhí)行 SET counter:order max(M, N)將最大值寫(xiě)入 Redis,相等的話就不操作。
  1. 應(yīng)用服務(wù)啟動(dòng)完成后,每次需要生成 ID 的時(shí)候,應(yīng)用程序就向 Redis 服務(wù)器發(fā)送 INCR counter:order指令。
  2. 應(yīng)用程序?qū)@取到的 ID 值發(fā)送到 MQ 消息隊(duì)列,消費(fèi)者監(jiān)聽(tīng)隊(duì)列把值更新到 MySQL。

SpringBoot 代碼實(shí)現(xiàn)

接下來(lái),我們結(jié)合 SpringBoot3.0 來(lái)打造一個(gè)強(qiáng)大且易用的 Redis 分布式 ID 生成器。

首先,我們需要在 SpringBoot 項(xiàng)目中引入 redis 的依賴(lài)。在pom.xml文件中添上這行代碼:

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

搞定依賴(lài)后,我們得告訴 SpringBoot 怎么連接到 Redis。打開(kāi)application.yml文件,填上 Redis 的服務(wù)地址和端口:

spring:
  application:
    name: redis
  redis:
    host: 127.0.0.1
    port: 6379
    password: magebyte
    timeout: 6000

萬(wàn)事俱備,只欠東風(fēng)!接下來(lái),我們編寫(xiě)一個(gè) ID 生成器工具類(lèi)。這個(gè)工具類(lèi)負(fù)責(zé)與 Redis 交互,生成唯一的 ID。這里我們使用 Redis 的INCR命令,它能讓 ID 自增,確保每次獲取的 ID 都是唯一的。

@Component
public class OrderIdGenerator implements InitializingBean {

    private final StringRedisTemplate redisTemplate;

    /**
     * 操作數(shù)據(jù)庫(kù) dao
     */
    private final IdGeneratorMapper idGeneratorMapper;

    private static final String KEY = "counter:order";

    /**
     * 數(shù)據(jù)庫(kù)中的 ID 值
     */
    private String dbId;

    @Autowired
    public OrderIdGenerator(StringRedisTemplate redisTemplate, IdGeneratorMapper idGeneratorMapper) {
        this.redisTemplate = redisTemplate;
        this.idGeneratorMapper = idGeneratorMapper;
    }

    public Long generateId(String key) {
        return redisTemplate.opsForValue().increment(key, 1);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        // 從數(shù)據(jù)庫(kù)查詢最大 ID
        this.dbId = idGeneratorMapper.getMaxID(KEY);

        Boolean hasKey = redisTemplate.hasKey(KEY);
        if (Boolean.TRUE.equals(hasKey)) {
            // key 存在,比較 dbId 與 redisValue,取出最大值
            String redisValue = redisTemplate.opsForValue().get(KEY);
            String targetValue = max(this.dbId, redisValue);
        } else {

自定義 ID 規(guī)則

不過(guò)呢,光有唯一的 ID 還不夠,我們還得讓它更符合業(yè)務(wù)的實(shí)際需求。比如訂單編號(hào)吧,我們可能希望它的格式是ORD-20240528-0001,其中ORD是業(yè)務(wù)標(biāo)識(shí),20240528是日期,0001是當(dāng)天的序號(hào)。

public String generateCustomId(String key, String prefix, String datePattern) {
    long sequence = redisTemplate.opsForValue().increment(key, 1);
    return String.format("%s-%s-%04d", prefix, new SimpleDateFormat(datePattern).format(new Date()), sequence);
}

那具體怎么用呢?讓我們?cè)跇I(yè)務(wù)代碼中一探究竟!想象一下,在一個(gè)電商系統(tǒng)中,當(dāng)一個(gè)新的訂單如流星般劃過(guò)天際,我們迫不及待地想要一個(gè)獨(dú)一無(wú)二的 ID 來(lái)標(biāo)記它時(shí)——很簡(jiǎn)單,只需調(diào)用我們的generateCustomId方法,傳入訂單相關(guān)的參數(shù)即可。

@Service
public class OrderService {
    private final RedisIdGenerator idGenerator;

    @Autowired
    public OrderService(RedisIdGenerator idGenerator) {
        this.idGenerator = idativeIdGenerator;
    }

    public Order createOrder(OrderRequest request) {
        String orderId = idGenerator.generateCustomId("order:id", "ORD", "yyyyMMdd");
        Order order = new Order();
        order.setId(orderId);
        // 其他業(yè)務(wù)邏輯...
        return order;
    }
}
責(zé)任編輯:姜華 來(lái)源: 碼哥跳動(dòng)
相關(guān)推薦

2022-12-29 08:43:54

IDredis

2017-07-01 16:02:39

分布式ID生成器

2019-12-27 10:00:34

開(kāi)源技術(shù) 軟件

2022-07-25 10:27:36

背景生成器工具前端

2024-01-26 08:28:41

工單號(hào)生成器場(chǎng)景

2022-03-03 08:30:41

GeneratorES6函數(shù)

2025-03-11 08:50:00

CASID分布式

2020-08-21 13:15:29

開(kāi)發(fā)技能代碼

2022-02-15 10:30:58

UUID

2015-08-25 11:07:58

2025-01-23 08:36:27

CSS開(kāi)發(fā)工具

2010-09-07 16:31:17

SQL語(yǔ)句insert

2021-04-22 21:15:38

Generator函數(shù)生成器

2011-12-23 13:42:05

JavaScript

2025-01-06 09:06:04

JavaScriptWeb 開(kāi)發(fā)Generators

2016-01-08 10:55:31

PHP莫斯電碼生成器

2016-12-21 10:55:55

PHP莫斯電碼生成器

2017-03-20 17:49:21

Java Web模板代碼

2021-07-14 07:17:37

Springboot分布式UIDGenerato

2024-11-01 15:51:06

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)