本文源碼:GitHub·點這裏 || GitEE·點這裏java
在業務系統中,查詢時最容易出現性能問題的模塊,查詢面對的數據量大,篩選條件複雜,因此在系統架構中引入緩存層,則是很是必要的,用來緩存熱點數據,達到快速響應的目的。git
緩存使用的基本原則:github
這裏是業務架構中經常使用的緩存策略,緩存經過犧牲強一致性來提升性能,因此並非全部的業務都適合用緩存,實際考量都會針對具體的業務,好比用戶相關維度的數據修改頻率低,會使用緩存,可是用戶權限數據(好比:免費次數)會考慮實時校驗,緩存層使用的相對較少。redis
Cache-Aside模式算法
業務中最經常使用的緩存層設計模式,基本實現邏輯和相關概念以下:數據庫
執行讀操做未命中緩存,而後查詢數據庫中取數據,數據已經查詢到還沒放入緩存,同時一個更新寫操做讓緩存失效,而後讀操做再把查詢到數據加載緩存,致使緩存的髒數據。設計模式
在遵照緩存使用原則下出現該狀況機率很是低,能夠經過複雜的Paxos協議保證一致性,通常狀況是不考量該場景的處理,若是緩存管理過於複雜,會和緩存層核心理念相悖。數組
基本描述代碼:緩存
@Service public class KeyValueServiceImpl extends ServiceImpl<KeyValueMapper, KeyValueEntity> implements KeyValueService { @Resource private RedisService redisService ; @Override public KeyValueEntity select(Integer id) { // 查詢緩存 String redisKey = RedisKeyUtil.getObectKey(id) ; String value = redisService.get(redisKey) ; if (!StringUtils.isEmpty(value) && !value.equals("null")){ return JSON.parseObject(value,KeyValueEntity.class); } // 查詢庫 KeyValueEntity keyValueEntity = this.getById(id) ; if (keyValueEntity != null){ // 緩存寫入 redisService.set(redisKey,JSON.toJSONString(keyValueEntity)) ; } // 返回值 return keyValueEntity ; } @Override public boolean update(KeyValueEntity keyValueEntity) { // 更新數據 boolean updateFlag = this.updateById(keyValueEntity) ; // 清除緩存 if (updateFlag){ redisService.delete(RedisKeyUtil.getObectKey(keyValueEntity.getId())); } return updateFlag ; } }
Read-Throug模式服務器
當應用系統向緩存系統請求數據時,若是緩存中並無對應的數據存在,緩存系統將向底層數據源的讀取數據。若是數據在緩存中存在,則直接返回緩存中存在的數據。把更新數據庫的操做由緩存層代勞了。
Write-Through模式
更新寫數據時,若是沒有命中緩存,則直接更新數據庫,若是命中了緩存,則先更新緩存,而後由緩存系統自行更新數據庫。
Write-Behind模式
應用系統對緩存中的數據進行更新時,只更新緩存,不更新數據庫,緩存系統會異步批量向底層數據源更新數據。
業務開發模式中,會涉及到一個問題:如何最大限度保證數據庫和Redis緩存的數據一致性?
首先說明一下:數據庫和緩存強一致性同步成本過高,若是追求強一致,緩存層存在的價值就會很低,如上緩存模式一中幾乎能夠解決大部分業務場景問題。
解決這個問題的方式不少:
方案一說明:
分析:消息隊列的穩定和可靠性,操做層面數據庫和緩存層解耦。
方案二說明:
分析:系統架構層面多出一個服務,且須要解析MySQL日誌,操做難度較大,但流程上更爲合理。
總結描述
分佈式架構中,緩存層面的基本需求就是提升響應速度,不斷優化,追求數據庫和Redis緩存的數據快速一致性,從提供的各類方案中均可以看出,這也在增長緩存層面處理的複雜性,架構邏輯複雜,就容易致使程序錯誤,因此針對業務選擇合理的處理邏輯,這點很關鍵。
經過info命令查看Redis服務的參數信息,能夠經過傳參查看指定分類配置。經過config..set設置具體配置參數。例如:
@Override public Properties info(String var) { if (StringUtils.isEmpty(var)){ return redisTemplate.getRequiredConnectionFactory().getConnection().info(); } return redisTemplate.getRequiredConnectionFactory().getConnection().info(var); }
傳參說明:
應用案例:
@RestController public class MonitorController { @Resource private RedisService redisService ; private static final String[] monitorParam = new String[]{"memory","server","clients","stats","cpu"} ; @GetMapping("/monitor") public List<MonitorEntity> monitor (){ List<MonitorEntity> monitorEntityList = new ArrayList<>() ; for (String param:monitorParam){ Properties properties = redisService.info(param) ; MonitorEntity monitorEntity = new MonitorEntity () ; monitorEntity.setMonitorParam(param); monitorEntity.setProperties(properties); monitorEntityList.add(monitorEntity); } return monitorEntityList ; } }
經過上述參數組合,把Redis相關配置參數打印出來,而後可視化輸出,儼然一副高端的感受。
配置參數說明:
這裏只對兩個參數說明一下,計算命中率的關鍵信息:
公式:命中率=命中次數/(hits+misses)查找總次數。
Redis的數據是放在內存中的,因此速度快,天然也就受到內存大小的限制,若是內存使用超過配置,Redis有不一樣的回收處理策略。
內存模塊參數:maxmemory_policy
大部分狀況下,業務都是但願最熱點數據能夠被緩存,因此相對使用allkeys-lru策略偏多。這裏要根據業務模式特色衡量。
GitHub·地址 https://github.com/cicadasmile/data-manage-parent GitEE·地址 https://gitee.com/cicadasmile/data-manage-parent
推薦閱讀:《架構設計系列》,蘿蔔青菜,各有所需
序號 | 標題 |
---|---|
01 | 架構基礎:單服務.集羣.分佈式,基本區別和聯繫 |
02 | 架構設計:分佈式業務系統中,全局ID生成策略 |
03 | 架構設計:分佈式系統調度,Zookeeper集羣化管理 |
04 | 架構設計:接口冪等性原則,防重複提交Token管理 |