首頁 > 資料庫 > Redis > Redis分散式快取與秒殺怎麼實現

Redis分散式快取與秒殺怎麼實現

王林
發布: 2023-06-02 23:43:20
轉載
1173 人瀏覽過

一、單點Redis的問題

1、資料遺失問題

Redis資料持久化。

2、並發能力問題

大家主從集群,實現讀寫分離。

3、故障復原問題

利用Redis哨兵,實現健康偵測與自動復原。

4、儲存能力問題

建置分片集群,利用插槽機制實現動態擴充。

二、RDB

RDB全名為Redis Database Backup file(Redis資料備份檔),也被叫做Redis資料快照。簡單來說就是把記憶體中的所有資料都記錄到磁碟中。當Redis實例故障重啟後,從磁碟讀取快照文件,恢復資料。
快照檔案稱為RDB文件,預設是儲存在目前運行目錄。

Redis內部有觸發RDB的機制,可以在redis.conf檔中找到,格式如下:

Redis分散式快取與秒殺怎麼實現

當執行bgsave指令時,會透過fork系統呼叫建立一個子進程,該子進程與主進程共享記憶體資料。完成fork後讀取記憶體資料並寫入 RDB 檔案。

fork採用的是copy-on-write技術:

  • 當主程序執行讀取操作時,存取共享記憶體;

  • 當主程序執行寫入操作時,則會拷貝一份數據,執行寫入操作;

RDB方式bgsave的基本流程?

  1. fork主程序得到一個子程序,共享記憶體空間;

  2. #子程序讀取記憶體資料並寫入新的RDB檔;

  3. 用新RDB檔案取代舊的RDB檔案;

Redis分散式快取與秒殺怎麼實現

#RDB會在什麼時候執行? save 60 1000代表什麼意義?

  • 預設是服務停止時;

  • 代表60秒內至少執行1000次修改則觸發RDB;

RDB的缺點?

  • RDB執行間隔時間長,兩次RDB之間寫入資料有遺失的風險;

  • fork子程序、壓縮、寫出RDB檔案都比較耗時;

AOF的指令記錄的頻率也可以透過redis.conf檔來配:

三、AOF

AOF全稱為Append Only File(追加檔案)。 Redis處理的每一個寫入指令都會記錄在AOF文件,可以看做是指令日誌檔。

AOF預設是關閉的,需要修改redis.conf設定檔來開啟AOF:

Redis分散式快取與秒殺怎麼實現

#AOF的指令記錄的頻率也可以透過redis.conf檔案來配:

Redis分散式快取與秒殺怎麼實現

##優點#缺點Always同步刷盤 可靠度高,幾乎不遺失資料效能影響大everysec每秒刷盤效能適中最多一分鐘的資料no作業系統控制效能最好可靠性較差,可能會遺失大量資料

 因為是記錄指令,AOF檔會比RDB檔案大的多。雖然AOF會記錄對同一個鍵的多次寫入操作,但是它們中只有最後一次寫入操作是有意義的。可以使用bgrewriteaof指令,以最小的指令數完成AOF檔重寫的功能。

set id 1
set name nezha
set id 2

bgrewriteaof

mset name nezha id 2
登入後複製

Redis也會在觸發閾值時自動去重寫AOF檔。閾值也可以在redis.conf中配置:

AOF檔案比上次檔案增長超過多少百分比則觸發重寫auto-aof-rewrite-percentage 100# AOF檔案體積最小多大以上才觸發重寫auto-aof-rewrite-min-size 64mb 

 RDB和AOF各有自己的優缺點,如果對資料安全性要求較高,實際開發中往往會結合兩者來使用。

配置項目 刷盤時機
##檔案大小會有壓縮,檔案體積小記錄指令,檔案體積很大宕機恢復速度很快##數據復原優先權低,因為資料完整性不低高,因為資料完整性較高系統資源佔用#高,大量CPU和記憶體消耗低,主要是磁碟IO資源,但AOF重寫時會佔用大量CPU和記憶體資源使用場景可以容忍數分鐘的資料遺失,追求更快的啟動速度對資料安全性要求較高常見

RDB#AOF
持久化方式 定時對整個記憶體做快照記錄每一次執行的命令
#資料完整性不完整,兩次備份之間會遺失相對完整,取決於刷盤策略
檔案大小會有壓縮,檔案體積小
##慢

##四、 Redis優化秒殺流程

    1、秒殺步驟:
  1. 查詢優惠券;
  2. 判斷秒殺商品庫存;
  3. 查詢訂單
  4. #校驗一人一單;
  5. ##減去庫存;

Redis分散式快取與秒殺怎麼實現

  1. ##建立訂單;

  2. #2、Redis最佳化秒殺步驟:

Redis分散式快取與秒殺怎麼實現

新增秒殺的優惠券,將優惠券資訊儲存到Redis;

Redis分散式快取與秒殺怎麼實現基於Lua腳本,判斷秒殺商品庫存,一人一單,決定用戶是否秒殺成功;

如果秒殺成功,將優惠券id、使用者id、商品id封裝到阻塞佇列中;

開啟非同步任務,不斷從阻塞佇列中讀取訊息,實現非同步下單一功能;

Redis分散式快取與秒殺怎麼實現

#3、秒殺的lua腳本Redis分散式快取與秒殺怎麼實現

###### ## 4、呼叫秒殺的lua腳本###
public Result seckillVoucher(Long voucherId) {
     Long userId = UserHolder.getUser().getId();
     long orderId = redisIdWorker.nextId("order");
     // 1.执行lua脚本
     Long result = stringRedisTemplate.execute(
             SECKILL_SCRIPT,
             Collections.emptyList(),
             voucherId.toString(), userId.toString(), String.valueOf(orderId)
     );
     int r = result.intValue();
     // 2.判断结果是否为0
     if (r != 0) {
         // 2.1.不为0 ,代表没有购买资格
         return Result.fail(r == 1 ? "库存不足" : "不能重复下单");
     }
     // 3.返回订单id
     return Result.ok(orderId);
 }
登入後複製
###5、透過執行緒池,操作阻塞佇列###
// 线程池
private static final ExecutorService SECKILL_ORDER_EXECUTOR = Executors.newSingleThreadExecutor();

/**
* 在类初始化完成后执行
*/
@PostConstruct
private void init() {
    SECKILL_ORDER_EXECUTOR.submit(new VoucherOrderHandler());
}

// 阻塞队列
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實作共用session登入######基於session實作登入###############基於Redis實作共享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、获取请求头中的token
        String token = request.getHeader("authorization");
        if (StrUtil.isBlank(token)) {
            return true;
        }
        // 2、基于TOKEN获取redis中的用户
        String key  = LOGIN_USER_KEY + token;
        Map<Object, Object> userMap = stringRedisTemplate.opsForHash().entries(key);
        // 3、判断用户是否存在
        if (userMap.isEmpty()) {
            return true;
        }
        // 5、将查询到的hash数据转为UserDTO
        UserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);
        // 6、存在,保存用户信息到 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 {
        // 移除用户
        UserHolder.removeUser();
    }
}
登入後複製
#########

以上是Redis分散式快取與秒殺怎麼實現的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:yisu.com
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板