如何使用redis生成流水號 redis持久化

概述

本文講述如何使用redis生成流水號医吊。本文是在Springboot中實現(xiàn)的。知道原理之后其他框架也可以輕松實現(xiàn)逮京。

原理介紹

本文主要是使用redis的incr方法進行自增補零卿堂。然后結(jié)合時間、隨機數(shù)懒棉、前綴組成唯一的流水號草描。

在這里插入圖片描述

下面是流水號的結(jié)構(gòu)。


在這里插入圖片描述

在文章的最后還是簡單介紹一下redis的持久化策严。防止redis宕機導(dǎo)致自增數(shù)重置穗慕。從而導(dǎo)致流水號重復(fù)。


流程圖

本次實現(xiàn)分為
1妻导、初始化流水號信息進入緩存
2逛绵、生成流水號

下面是分別的大致流程圖

1、初始化流水號信息進入緩存

在這里插入圖片描述

2倔韭、生成流水號

在這里插入圖片描述

代碼實現(xiàn)

1术浪、流水號實體類

用于存儲流水號信息


@Data
public class YuSnGenCode {
    // 流水號生成依賴的類,會使用類的名稱作為redis的key 
    private Class entity;
    // 前綴
    private String prefix;
    // 自增數(shù)位數(shù) 自增數(shù)達(dá)不到此位數(shù)自動補零
    private Integer num;

    public YuSnGenCode(Class entity, String prefix, Integer num) {
        this.entity = entity;
        this.prefix = prefix;
        this.num = num;
    }
}

2寿酌、初始化流水號進入緩存

在程序啟動之后初始化流水號


@Component
@Slf4j
public class YuSnGenStart implements ApplicationRunner {
    @Autowired
    YuSnGenUtil yuSnGenUtil;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        log.info("=====開始初始化流水號=====");
        List<YuSnGenCode> yuSnGenCodeList = new ArrayList<>();
        yuSnGenCodeList.add(new YuSnGenCode(Account.class, "AC", 6));
        yuSnGenCodeList.add(new YuSnGenCode(Shop.class, "DP", 6));
        yuSnGenCodeList.add(new YuSnGenCode(Order.class, "OR", 6));
        yuSnGenCodeList.add(new YuSnGenCode(Dispatch.class, "DL", 6));
        yuSnGenCodeList.add(new YuSnGenCode(Refund.class, "RF", 6));
        yuSnGenCodeList.add(new YuSnGenCode(PayLog.class, "PL", 6));
        yuSnGenCodeList.add(new YuSnGenCode(Withdrawal.class, "WR", 6));
        yuSnGenCodeList.add(new YuSnGenCode(Aftermarket.class, "AS", 6));
        yuSnGenCodeList.add(new YuSnGenCode(Coupon.class, "CO", 6));
        yuSnGenCodeList.add(new YuSnGenCode(CouponGen.class, "CG", 10));
        yuSnGenCodeList.add(new YuSnGenCode(Draw.class, "DP", 6));
        yuSnGenUtil.init(yuSnGenCodeList);
        log.info("=====初始化流水號完畢=====");
    }
}
  public void init(List<YuSnGenCode> yuSnGenCodes) {
        // 流水號初始化入緩存
        for (YuSnGenCode yuSnGenCode : yuSnGenCodes) {
            String redisKey = yuSnGenCode.getEntity().getName();
            // 存入緩存 key:key:實體類名稱 value:流水號數(shù)據(jù)(前綴胰苏、自增數(shù)位數(shù))
            redisTemplate.opsForValue().set(yuSnGenCode.getEntity().getName(), FastJsonUtils.toJsonStr(yuSnGenCode));
            log.info(yuSnGenCode.getEntity().getName() + "已初始化");

        }
    }

初始化之后的流水號如下圖所示


在這里插入圖片描述

3、生成流水號

   public Optional<String> gen(Class c) {
   // 獲取實體類的名稱
        String redisKey = c.getName();
        // 判斷是不是有初始化此實體類
        if (null != redisTemplate.opsForValue().get(redisKey)) {
        // 從緩存獲取流水號的生成信息
            YuSnGenCode yuSnGenCode = FastJsonUtils.toBean(redisTemplate.opsForValue().get(redisKey).toString(), YuSnGenCode.class);
         // 根據(jù)流水號的前綴判斷今天是否有生成過流水號
            if (redisTemplate.opsForValue().get(yuSnGenCode.getPrefix()) == null) {
                // 沒有則新建一個存入緩存 格式(key:OR  value:0)
                // 設(shè)置到第二天早上00:00:01過期
                Long todayTime = LocalDate.now().plusDays(1).atTime(0, 0, 0, 1).atOffset(ZoneOffset.ofHours(8)).toEpochSecond();
                Long nowTime = LocalDateTime.now().atOffset(ZoneOffset.ofHours(8)).toEpochSecond();
                Long expireTime = todayTime - nowTime;
                redisTemplate.opsForValue().set(yuSnGenCode.getPrefix(), 0, expireTime*1000, TimeUnit.MILLISECONDS);
            }
            // 進行自增操作
            StringBuffer sn = new StringBuffer();
            // 和前綴醇疼、時間硕并、隨機數(shù)進行組合
            sn.append(yuSnGenCode.getPrefix());
            String date = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
            sn.append(date);
            Long num = redisTemplate.opsForValue().increment(yuSnGenCode.getPrefix());
            sn.append(addZero(String.valueOf(num), yuSnGenCode.getNum()));
            String random = String.valueOf(new Random().nextInt(1000));
            sn.append(random);
            // 生成最終的流水號返回
            return Optional.ofNullable(sn.toString());
        }
        return Optional.ofNullable(null);
    }
 // 自動補零
 public String addZero(String numStr, Integer maxNum) {
        int addNum = maxNum - numStr.length();
        StringBuffer rStr = new StringBuffer();
        for (int i = 0; i < addNum; i++) {
            rStr.append("0");
        }
        rStr.append(numStr);
        return rStr.toString();
    }

第一次生成流水號之后redis中會有一個以此流水號的前綴作為key的數(shù)據(jù),后續(xù)直接進行自增僵腺。無需新增


在這里插入圖片描述

代碼測試

普通測試

   public Response test2() {
        return Response.success(yuSnGenUtil.gen(Order.class).get());
    }

結(jié)果如下


在這里插入圖片描述

并發(fā)測試

這里我們使用ab進行壓力并發(fā)測試
測試前我們流水號數(shù)據(jù)為0


在這里插入圖片描述

進行5000請求100并發(fā)的測試


在這里插入圖片描述

請求完畢之后流水號的數(shù)據(jù)是5000是沒有問題的鲤孵。
在這里插入圖片描述

redis 持久化

redis持久化指redis意外退出之后重啟仍然能夠恢復(fù)之前數(shù)據(jù)壶栋。我們這里使用redis持久化防止redis意外退出重啟導(dǎo)致流水號數(shù)據(jù)重置辰如,從而導(dǎo)致我們的流水號生成重復(fù)。

Redis 提供了不同級別的持久化方式:

RDB

RDB持久化方式能夠在指定的時間間隔能對你的數(shù)據(jù)進行快照存儲.

AOF

AOF持久化方式記錄每次對服務(wù)器寫的操作,當(dāng)服務(wù)器重啟的時候會重新執(zhí)行這些命令來恢復(fù)原始的數(shù)據(jù)

我們本次使用的是AOF的方式

在這里插入圖片描述

根據(jù)官網(wǎng)的提示贵试。我們只需要去redis.config文件中開啟appendonly就可以了琉兜。這樣redis會生成appendonly.aof文件,服務(wù)器重啟的時候會重新執(zhí)行這些命令來恢復(fù)原始的數(shù)據(jù)毙玻。
在這里插入圖片描述

注意

因為AOF的默認(rèn)備份方式有三種

1豌蟋、always每次有新命令追加到 AOF 文件時就執(zhí)行一次 fsync :非常慢,也非常安全
2桑滩、everysec每秒 fsync 一次:足夠快(和使用 RDB 持久化差不多)梧疲,并且在故障時只會丟失 1 秒鐘的數(shù)據(jù)。
3、no從不 fsync :將數(shù)據(jù)交給操作系統(tǒng)來處理幌氮。更快缭受,也更不安全的選擇。
==而我們采用的是第二種该互。也是AOF默認(rèn)的的米者。但是這樣故障時可能會損失1s的數(shù)據(jù),所以如果對數(shù)據(jù)要求十分嚴(yán)格的同學(xué)可以采用第一種方式只需要修改redis.config文件的appendfsync屬性即可宇智。==

在這里插入圖片描述

以上就是今天介紹的利用redis的incr自增方法編寫生成流水號蔓搞。如果有什么問題歡迎評論區(qū)指出。謝謝??

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末随橘,一起剝皮案震驚了整個濱河市喂分,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌机蔗,老刑警劉巖妻顶,帶你破解...
    沈念sama閱讀 212,816評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異蜒车,居然都是意外死亡讳嘱,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評論 3 385
  • 文/潘曉璐 我一進店門酿愧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來沥潭,“玉大人,你說我怎么就攤上這事嬉挡《鄹耄” “怎么了?”我有些...
    開封第一講書人閱讀 158,300評論 0 348
  • 文/不壞的土叔 我叫張陵庞钢,是天一觀的道長拔恰。 經(jīng)常有香客問我,道長基括,這世上最難降的妖魔是什么颜懊? 我笑而不...
    開封第一講書人閱讀 56,780評論 1 285
  • 正文 為了忘掉前任,我火速辦了婚禮风皿,結(jié)果婚禮上河爹,老公的妹妹穿的比我還像新娘。我一直安慰自己桐款,他們只是感情好咸这,可當(dāng)我...
    茶點故事閱讀 65,890評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著魔眨,像睡著了一般媳维。 火紅的嫁衣襯著肌膚如雪酿雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,084評論 1 291
  • 那天侄刽,我揣著相機與錄音执虹,去河邊找鬼。 笑死唠梨,一個胖子當(dāng)著我的面吹牛袋励,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播当叭,決...
    沈念sama閱讀 39,151評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼茬故,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了蚁鳖?” 一聲冷哼從身側(cè)響起磺芭,我...
    開封第一講書人閱讀 37,912評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎醉箕,沒想到半個月后钾腺,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,355評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡讥裤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,666評論 2 327
  • 正文 我和宋清朗相戀三年放棒,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片己英。...
    茶點故事閱讀 38,809評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡间螟,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出损肛,到底是詐尸還是另有隱情厢破,我是刑警寧澤,帶...
    沈念sama閱讀 34,504評論 4 334
  • 正文 年R本政府宣布治拿,位于F島的核電站摩泪,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏劫谅。R本人自食惡果不足惜见坑,卻給世界環(huán)境...
    茶點故事閱讀 40,150評論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望同波。 院中可真熱鬧鳄梅,春花似錦叠国、人聲如沸未檩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽冤狡。三九已至孙蒙,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間悲雳,已是汗流浹背挎峦。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留合瓢,地道東北人坦胶。 一個月前我還...
    沈念sama閱讀 46,628評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像晴楔,于是被迫代替她去往敵國和親顿苇。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,724評論 2 351