Redis分布式事务误区

        今天做一个抽奖相关的需求,需要用到缓存,自然就想到了redis事务相关的操作。但是编码的时候发现jedis-2.1.0.jar并没有redis.watch()、redis.multi()这些事务相关的接口,来撸起袖子扒源码~

        1、我们使用的是客户端分片的方式,使用jedis-2.1.0.jar,数据的操作均使用ShardedJedis进行操作,扒开第一层。sharedjedis的写入查询均是通过Jedis j = getShard(key);获取jedis之后再进行操作,断开连接需要for循环遍历断开所有的shard。


Redis分布式事务误区_第1张图片

2、getShard(key) 实际是调用Sharded父类的接口。Sharded 构造函数会调用初始化的接口,按照分片算法生成节点信息(hash环),后面的getShard 实际是对hash(key)之后根据一致性hash环的算法确定key落到哪个node。当需要根据shardjedis进行查询和写入数据时,实际是根据hash算法计算key实际落到哪个node,然后再根据node可以确定数据写到哪个redis机器。

Redis分布式事务误区_第2张图片

3、2中的getShard 实际最后是根据node获取了shardInfo,然后在根据 Map resources集合获取对应的jedis。resources.get(getShardInfo(key))实际返回的是shardInfo.createResource()值。再看看createResource()——》实际是JedisShardInfo—> return new Jedis(this);

resources.put(shardInfo, shardInfo.createResource());//2中初始化put的value是shardInfo.createResource()

public R getShard(String key) {return resources.get(getShardInfo(key));}//根据分片获取连接

Redis分布式事务误区_第3张图片

实际是return new Jedis(this);

Redis分布式事务误区_第4张图片

实际是BinaryJedis的构造函数获取client连接

Redis分布式事务误区_第5张图片

4、所以根据上面的源码分析,sharedjedis 操作redis集群,实际上是根据key获取分片,最终获取了单个redis实例之后进行的操作。我们再看看jedis的代码,发现并无multi()接口,并且其父类BinaryJedis里面有multi()接口,调用multi()会将整个client的连接重置为isInMulti=true的状态,断开连接以及exec执行完毕会重置为false.

Redis分布式事务误区_第6张图片

5、BinaryJedis、Jedis 接口都会调用checkIsInMulti();判断是否是multi()操作,若是则抛出异常 throw new JedisDataException( "Cannot use Jedis when in Multi. Please use JedisTransaction instead.");配合(4)的逻辑来看,这个场景的意思是在此client已经调用multi()之后,后面的操作不能直接调用jedis的进行操作,而是需要调用事务jedisTransaction 进行操作。所以redis的事务是通过单个redisclient来控制的。

    redis集群若想实现多key的事务控制,可能根据分片算法会分配到不同的jedisclient,导致无法进行事务控制,这应该就是redis集群不支持事务的核心原因。但是若是单个key的get()获取值之后再set(),可以使用单个jedis连接的watch、multi()、exec()来实现,防止get()之后其它client又set()导致数据和预期不一致的场景。

Redis分布式事务误区_第7张图片

    总结:由于redis事务是通过单个jedis连接来进行控制的,所以redis集群情况有多个分片,多key可能会分片到多个不同的连接上导致无法进行事务控制;单key的场景由于可以根据key分片确定落到固定连接上,故可以使用jedis的multi进行事务控制。

你可能感兴趣的:(Redis分布式事务误区)