溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

Redis分布式緩存與秒殺怎么實(shí)現(xiàn)

發(fā)布時(shí)間:2023-04-03 11:29:06 來(lái)源:億速云 閱讀:107 作者:iii 欄目:開(kāi)發(fā)技術(shù)

本篇內(nèi)容介紹了“Redis分布式緩存與秒殺怎么實(shí)現(xiàn)”的有關(guān)知識(shí),在實(shí)際案例的操作過(guò)程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

一、單點(diǎn)Redis的問(wèn)題

1、數(shù)據(jù)丟失問(wèn)題

Redis數(shù)據(jù)持久化。

2、并發(fā)能力問(wèn)題

大家主從集群,實(shí)現(xiàn)讀寫(xiě)分離。

3、故障恢復(fù)問(wèn)題

利用Redis哨兵,實(shí)現(xiàn)健康檢測(cè)和自動(dòng)恢復(fù)。

4、存儲(chǔ)能力問(wèn)題

搭建分片集群,利用插槽機(jī)制實(shí)現(xiàn)動(dòng)態(tài)擴(kuò)容。

二、RDB

RDB全稱(chēng)Redis Database Backup file(Redis數(shù)據(jù)備份文件),也被叫做Redis數(shù)據(jù)快照。簡(jiǎn)單來(lái)說(shuō)就是把內(nèi)存中的所有數(shù)據(jù)都記錄到磁盤(pán)中。當(dāng)Redis實(shí)例故障重啟后,從磁盤(pán)讀取快照文件,恢復(fù)數(shù)據(jù)。
快照文件稱(chēng)為RDB文件,默認(rèn)是保存在當(dāng)前運(yùn)行目錄。

Redis內(nèi)部有觸發(fā)RDB的機(jī)制,可以在redis.conf文件中找到,格式如下:

Redis分布式緩存與秒殺怎么實(shí)現(xiàn)

bgsave開(kāi)始時(shí)會(huì)fork主進(jìn)程得到子進(jìn)程,子進(jìn)程共享主進(jìn)程的內(nèi)存數(shù)據(jù)。完成fork后讀取內(nèi)存數(shù)據(jù)并寫(xiě)入 RDB 文件。

fork采用的是copy-on-write技術(shù):

  • 當(dāng)主進(jìn)程執(zhí)行讀操作時(shí),訪(fǎng)問(wèn)共享內(nèi)存;

  • 當(dāng)主進(jìn)程執(zhí)行寫(xiě)操作時(shí),則會(huì)拷貝一份數(shù)據(jù),執(zhí)行寫(xiě)操作;

RDB方式bgsave的基本流程?

  1. fork主進(jìn)程得到一個(gè)子進(jìn)程,共享內(nèi)存空間;

  2. 子進(jìn)程讀取內(nèi)存數(shù)據(jù)并寫(xiě)入新的RDB文件;

  3. 用新RDB文件替換舊的RDB文件;

Redis分布式緩存與秒殺怎么實(shí)現(xiàn)

RDB會(huì)在什么時(shí)候執(zhí)行?save 60 1000代表什么含義?

  • 默認(rèn)是服務(wù)停止時(shí);

  • 代表60秒內(nèi)至少執(zhí)行1000次修改則觸發(fā)RDB;

RDB的缺點(diǎn)?

  • RDB執(zhí)行間隔時(shí)間長(zhǎng),兩次RDB之間寫(xiě)入數(shù)據(jù)有丟失的風(fēng)險(xiǎn);

  • fork子進(jìn)程、壓縮、寫(xiě)出RDB文件都比較耗時(shí);

AOF的命令記錄的頻率也可以通過(guò)redis.conf文件來(lái)配:

三、AOF

AOF全稱(chēng)為Append Only File(追加文件)。Redis處理的每一個(gè)寫(xiě)命令都會(huì)記錄在AOF文件,可以看做是命令日志文件。

AOF默認(rèn)是關(guān)閉的,需要修改redis.conf配置文件來(lái)開(kāi)啟AOF:

Redis分布式緩存與秒殺怎么實(shí)現(xiàn)

AOF的命令記錄的頻率也可以通過(guò)redis.conf文件來(lái)配:

Redis分布式緩存與秒殺怎么實(shí)現(xiàn)

配置項(xiàng)刷盤(pán)時(shí)機(jī)優(yōu)點(diǎn)缺點(diǎn)
Always同步刷盤(pán)可靠性高,幾乎不丟數(shù)據(jù)性能影響大
everysec每秒刷盤(pán)性能適中最多丟失一分鐘的數(shù)據(jù)
no操作系統(tǒng)控制性能最好可靠性較差,可能丟失大量數(shù)據(jù)

 因?yàn)槭怯涗浢睿珹OF文件會(huì)比RDB文件大的多。而且AOF會(huì)記錄對(duì)同一個(gè)key的多次寫(xiě)操作,但只有最后一次寫(xiě)操作才有意義。通過(guò)執(zhí)行bgrewriteaof命令,可以讓AOF文件執(zhí)行重寫(xiě)功能,用最少的命令達(dá)到相同效果。

set id 1
set name nezha
set id 2

bgrewriteaof

mset name nezha id 2

Redis也會(huì)在觸發(fā)閾值時(shí)自動(dòng)去重寫(xiě)AOF文件。閾值也可以在redis.conf中配置:

# AOF文件比上次文件 增長(zhǎng)超過(guò)多少百分比則觸發(fā)重寫(xiě)auto-aof-rewrite-percentage 100# AOF文件體積最小多大以上才觸發(fā)重寫(xiě) auto-aof-rewrite-min-size 64mb 

 RDB和AOF各有自己的優(yōu)缺點(diǎn),如果對(duì)數(shù)據(jù)安全性要求較高,在實(shí)際開(kāi)發(fā)中往往會(huì)結(jié)合兩者來(lái)使用。


RDBAOF
持久化方式定時(shí)對(duì)整個(gè)內(nèi)存做快照記錄每一次執(zhí)行的命令
數(shù)據(jù)完整性不完整,兩次備份之間會(huì)丟失相對(duì)完整,取決于刷盤(pán)策略
文件大小會(huì)有壓縮,文件體積小記錄命令,文件體積很大
宕機(jī)恢復(fù)速度很快
數(shù)據(jù)恢復(fù)優(yōu)先級(jí)低,因?yàn)閿?shù)據(jù)完整性不低高,因?yàn)閿?shù)據(jù)完整性更高
系統(tǒng)資源占用高,大量CPU和內(nèi)存消耗低,主要是磁盤(pán)IO資源,但AOF重寫(xiě)時(shí)會(huì)占用大量CPU和內(nèi)存資源
使用場(chǎng)景可以容忍數(shù)分鐘的數(shù)據(jù)丟失,追求更快的啟動(dòng)速度對(duì)數(shù)據(jù)安全性要求較高常見(jiàn)

四、Redis優(yōu)化秒殺流程

1、秒殺步驟:

  1. 查詢(xún)優(yōu)惠券;

  2. 判斷秒殺商品庫(kù)存;

  3. 查詢(xún)訂單

  4. 校驗(yàn)一人一單;

  5. 減庫(kù)存;

  6. 創(chuàng)建訂單;

Redis分布式緩存與秒殺怎么實(shí)現(xiàn)

2、Redis優(yōu)化秒殺步驟:

  1. 新增秒殺的優(yōu)惠券,將優(yōu)惠券信息保存到Redis中;

  2. 基于Lua腳本,判斷秒殺商品庫(kù)存,一人一單,決定用戶(hù)是否秒殺成功;

  3. 如果秒殺成功,將優(yōu)惠券id、用戶(hù)id、商品id封裝到阻塞隊(duì)列中;

  4. 開(kāi)啟異步任務(wù),不斷從阻塞隊(duì)列中讀取信息,實(shí)現(xiàn)異步下單功能;

Redis分布式緩存與秒殺怎么實(shí)現(xiàn)

3、秒殺的lua腳本

Redis分布式緩存與秒殺怎么實(shí)現(xiàn)

 4、調(diào)用秒殺的lua腳本

public Result seckillVoucher(Long voucherId) {
     Long userId = UserHolder.getUser().getId();
     long orderId = redisIdWorker.nextId("order");
     // 1.執(zhí)行l(wèi)ua腳本
     Long result = stringRedisTemplate.execute(
             SECKILL_SCRIPT,
             Collections.emptyList(),
             voucherId.toString(), userId.toString(), String.valueOf(orderId)
     );
     int r = result.intValue();
     // 2.判斷結(jié)果是否為0
     if (r != 0) {
         // 2.1.不為0 ,代表沒(méi)有購(gòu)買(mǎi)資格
         return Result.fail(r == 1 ? "庫(kù)存不足" : "不能重復(fù)下單");
     }
     // 3.返回訂單id
     return Result.ok(orderId);
 }

5、通過(guò)線(xiàn)程池,操作阻塞隊(duì)列

// 線(xiàn)程池
private static final ExecutorService SECKILL_ORDER_EXECUTOR = Executors.newSingleThreadExecutor();

/**
* 在類(lèi)初始化完成后執(zhí)行
*/
@PostConstruct
private void init() {
    SECKILL_ORDER_EXECUTOR.submit(new VoucherOrderHandler());
}

// 阻塞隊(duì)列
private BlockingQueue<VoucherOrder> orderTasks = new ArrayBlockingQueue<>(1024 * 1024);
private class OrderHandler implements Runnable{

    @Override
    public void run() {
        while (true){
            try {
                doSomething();
            } catch (Exception e) {
                log.error("處理訂單異常", e);
            }
        }
    }
}

五、基于Redis實(shí)現(xiàn)共享session登錄

基于session實(shí)現(xiàn)登錄

Redis分布式緩存與秒殺怎么實(shí)現(xiàn)

基于Redis實(shí)現(xiàn)共享session登錄

public class RefreshTokenInterceptor implements HandlerInterceptor {

    private StringRedisTemplate stringRedisTemplate;

    public RefreshTokenInterceptor(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 1、獲取請(qǐng)求頭中的token
        String token = request.getHeader("authorization");
        if (StrUtil.isBlank(token)) {
            return true;
        }
        // 2、基于TOKEN獲取redis中的用戶(hù)
        String key  = LOGIN_USER_KEY + token;
        Map<Object, Object> userMap = stringRedisTemplate.opsForHash().entries(key);
        // 3、判斷用戶(hù)是否存在
        if (userMap.isEmpty()) {
            return true;
        }
        // 5、將查詢(xún)到的hash數(shù)據(jù)轉(zhuǎn)為UserDTO
        UserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);
        // 6、存在,保存用戶(hù)信息到 ThreadLocal
        UserHolder.saveUser(userDTO);
        // 7、刷新token有效期
        stringRedisTemplate.expire(key, LOGIN_USER_TTL, TimeUnit.MINUTES);
        // 8、放行
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 移除用戶(hù)
        UserHolder.removeUser();
    }
}

Redis分布式緩存與秒殺怎么實(shí)現(xiàn)

“Redis分布式緩存與秒殺怎么實(shí)現(xiàn)”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI