Redis一致性方案

尽管到目前为止,我在项目中很少遇到缓存不一致问题,仅有一次是因为代码原因。就是那次让我考虑缓存不一致的解决方案,网上有很方案,例如加锁,消息队列,或延迟删除,还有监控binlog日志,以及用Lua实现乐观锁,但我以为这些方案都不是太理想,要么增加了系统的复杂度,要么不能做到实时一致性。一天我漫步在一个江南水乡的小镇上。突发奇想:很多方案都是利用串行化的方式实现缓存一致性。那我为什么不能利用数据库的特性去实现呢?于是有了以下方案:
首先关系数据库中对同一记录写与写之是互斥的,这表示一个写事条没有提交,另一个写事务将阻塞在哪里。那么这两个操作就是一个串行化操作了:

/**
**/
@Transactial() //定义整个方法体为事务边界
public void update(Entity e){
	data.update(e);			//先修改数据库,启动写事务
	redis.set(e.getId,e);
}

以上代码很轻松地解决了写与写之前的并发问题。那么写与读呢?
读数据流程通常是这样的:

public Entity get(int id){
	Entity e;
	if((e=readis.get(id))==null){
		e = dao.get(id);
		redis.put(id,e);
	}
	return e;
}

oracle数据默认隔离级别为:readcommit,在这种级别下,对同一条记录读与写可以并行执行。那么可能存在以下问题,在执get方法时,读到update方法中事务提交之前的数据,当update执行完毕后,get方法中put数据。这时候redis就会存在一条脏数据。mysql是Repeatable Read隔离级别,应该不会出现脏读问题吧(我没做过实验)。

对于读和写并行的问题我想是否可以让读操作中,对缓存只添加不修修改。redis提供的原子操作可以轻松实现这点:

public Entity get(int id){
	Entity e;
	if((e=readis.get(id))==null){
		e = dao.get(id);
		redis.putIfAbsent(id,e); //只当不存在该键时才put
	}
	return e;
}

最后一个问题,那就是删除,删除与读之间操作可能存在问题,如下图
在这里插入图片描述
如上图,可见在删与读之间可能脏数据问题。解决方案可以如下,删除时,不执行redis中的删除操作,而是执行put操作,将一个代表删除的对象put到redis中,并设制ttl时间为半分种。

public void delete(int id){
	dao.delete(id);
	redis.put(id,DELETED);
	redis.setTTL(id,30s)
}

以上是我自己琢磨出来的一套方案,但未付之行动。或许以有机会,我会去做一些实验。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值