本文最后更新于62 天前,其中的信息可能已经过时,如有错误请发送邮件到3368129372@qq.com
最近公司的kv数据库中间件由r2m切换至jimdb,特此记录
diff:
- jimdb采用哨兵模式探活;r2m由实例互相探测。
- jimdb采取nio;r2m基于jedis使用连接池(可能有连接风暴现象)
jimdb
- jimdb提供了节点抖动和故障检测、摘除功能
- jimdb支持入出流量控制
- 支持热key自动发现(封装的缓存对象中有该属性)
- 支持读写分离;支持轮询、随机负载均衡策略;支持客户端分组,每个分组使用不同策略
r2m
- 支持的指令更多
- 基于开源组件jedis
缓存迁移
- 缓存不重要,直接切换读与写。
- 不停写r2m,r2m不断同步数据到jimdb,切量读jimdb,完全读jimdb,校验数据一致性,读写jimdb。
- 停写,r2m迁移数据,读写jimdb。
- 双写,r2m同时进行迁移。
简要分析
方案一:看着就不太靠谱
方案二:
- 可能读到历史数据
- 在写流量切换的时候可能出现缓存不一致
r2m: t1 -> k = v1
jimdb: t2 -> k = v2
r2m->jimdb k=v1(最终)
或
r2m: t1 -> k 过期
jimdb: t2 -> k 更新过期状态
r2m->jimdb k 过期
方案三:
- 需要容忍长时间停写,中途还要校验数据一致性
方案四: goodgood
设计
- 对缓存所有的操作均应封装在一个公共类ext中,对于项目里所有不规范的写法做统一收口
- 写一个通用公共类,其中同时支持对jimdb与r2m的读写,策略由ducc来配置
- r2m在我们的系统存在两个集群,其为并列关系,此次排查这两个集群是否存在同一个key(查无),然后合并为一个集群。该集群分主从,下面称为jimdb主与jimdb从。
- 写策略同时支持:
- 读、写r2m
- 读、写jimdb主
- 读、写jimdb从
- 同时写r2m、jimdb主
- 同时写jimdb主、jimdb从
写两个集群时根据读策略异步写入另一个集群
- 读策略同时支持
- 读r2m
- 读jimdb主
- 读jimdb从
代码实现
- 对于缓存原始方法,封装公共的cacheHandler,根据策略不同做语义转换(两个中间件的方法名有很多不一致)。此时公共类的参数具有
- r2m实例
- jimdb实例
- 读写策略
- 线程池(用于异步操作)
初始化在客户端中进行
- 对于缓存的pipline方法则有所不同。若还是采取上述方法,spring使用同一个公共类实例则会产生线程安全问题。(a线程调用init方法, b线程close)。因此每次调用pipline时都需要new一个实例,其中excutor可共用。其实本质还是要模仿之前的写法。
- 既然读写时异步的,那close与sync也要是异步的。那么异步操作如何保证sync与close方法总是在最后执行呢?
- 最开始我打算使用countDownLatch实现,每次调用方法(例如setex)时-1,线程池中阻塞直到计数器为0。但这样需要手动传入一共需要执行的方法数量,客户端也得改。若出现方法出现在if中的情况:if(flag){setex;}则无法判断一共有几个操作
- 因此采用Phaser组件,每次调用方法时(同步)注册,调用完通知消费完毕。在线程池中调sync与close前,阻塞(异步)等待