1、資料遺失問題
Redis資料持久化。
2、並發能力問題
大家主從集群,實現讀寫分離。
3、故障復原問題
利用Redis哨兵,實現健康偵測與自動復原。
4、儲存能力問題
建置分片集群,利用插槽機制實現動態擴充。
RDB全名為Redis Database Backup file(Redis資料備份檔),也被叫做Redis資料快照。簡單來說就是把記憶體中的所有資料都記錄到磁碟中。當Redis實例故障重啟後,從磁碟讀取快照文件,恢復資料。
快照檔案稱為RDB文件,預設是儲存在目前運行目錄。
Redis內部有觸發RDB的機制,可以在redis.conf檔中找到,格式如下:
當執行bgsave指令時,會透過fork系統呼叫建立一個子進程,該子進程與主進程共享記憶體資料。完成fork後讀取記憶體資料並寫入 RDB 檔案。
fork採用的是copy-on-write技術:
當主程序執行讀取操作時,存取共享記憶體;
當主程序執行寫入操作時,則會拷貝一份數據,執行寫入操作;
RDB方式bgsave的基本流程?
fork主程序得到一個子程序,共享記憶體空間;
#子程序讀取記憶體資料並寫入新的RDB檔;
用新RDB檔案取代舊的RDB檔案;
#RDB會在什麼時候執行? save 60 1000代表什麼意義?
預設是服務停止時;
代表60秒內至少執行1000次修改則觸發RDB;
RDB的缺點?
RDB執行間隔時間長,兩次RDB之間寫入資料有遺失的風險;
fork子程序、壓縮、寫出RDB檔案都比較耗時;
AOF的指令記錄的頻率也可以透過redis.conf檔來配:
AOF全稱為Append Only File(追加檔案)。 Redis處理的每一個寫入指令都會記錄在AOF文件,可以看做是指令日誌檔。
AOF預設是關閉的,需要修改redis.conf設定檔來開啟AOF:
#AOF的指令記錄的頻率也可以透過redis.conf檔案來配:
配置項目 | 刷盤時機 | ##優點#缺點 | |
---|---|---|---|
同步刷盤 | 可靠度高,幾乎不遺失資料 | 效能影響大 | |
每秒刷盤 | 效能適中 | 最多一分鐘的資料 | |
作業系統控制 | 效能最好 | 可靠性較差,可能會遺失大量資料 |
RDB | #AOF | |
---|---|---|
持久化方式 | 定時對整個記憶體做快照 | 記錄每一次執行的命令 |
#資料完整性 | 不完整,兩次備份之間會遺失 | 相對完整,取決於刷盤策略 |
檔案大小 | 會有壓縮,檔案體積小 | ##檔案大小 |
記錄指令,檔案體積很大 | 宕機恢復速度 | |
##慢 | ##數據復原優先權 | 低,因為資料完整性不低 |
系統資源佔用 | #高,大量CPU和記憶體消耗 | |
使用場景 | 可以容忍數分鐘的資料遺失,追求更快的啟動速度 |
基於Lua腳本,判斷秒殺商品庫存,一人一單,決定用戶是否秒殺成功;
#3、秒殺的lua腳本
###### ## 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); }
// 线程池 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); } } } }
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中文網其他相關文章!