Redis学习笔记

一、Redis事务

Redis实现了基本的事务功能,但是不具有回滚功能。Redis通过使用 MULTIEXEC两个命令来开启事务,Redis会先缓存所有包围在事务中的命令,直到EXEC被调用才会将所有的命令一起发送给Redis服务。Redis保证事务的原子性和隔离性,也就是说事务中的命令要么全部执行,要么全部不执行,一但事务开始执行Redis服务会暂停其它客户单发送的请求,也就是事务中的命令按顺序执行并且不会被其它客户端打扰。
由于Redis事务执行过程中不加锁,所以多线程或者多客户单执行过程中会出现竞争条件。比如一个客户端A通过查询发现库存还有1个可用,于是下单,但是命令还未发往Redis服务,于此同时客户端B通过查询也发现库存还有1个可用,于是也下单,因为缓存或者网络等各种原因客户端B的命令可能会先于客户端A到达Redis服务,于是客户端B事务执行成功,客户端A产生了错误(但是它并没有意识到这点,也就是库存现在是负的)。为了解决这个问题,Redis使用了一个叫check-and-setWATCH命令,也就是乐观锁。沿用上面的例子,但是这次在使用事务之前先用WATCH命令监视库存,然后按照顺序执行,客户端B事务执行成功,客户端A事务执行失败(通过WATCH命令可以在执行事务的时候意识到库存已经被修改),此时客户端A可以重新执行事务,然后查询得知库存为0,于是放弃事务并通知用户。WATCH命令虽然可以保证数据正确,但是缺点是效率低,因为一但Redis负载高的话,冲突会指数上升,客户端需要不断的重复执行事务才有可能成功。

二、Redis锁

WATCH命令虽然可以保证数据的正确性,但是效率实在是不高,所以使用锁(排它性锁)将获得更高的效率。Redis并没有打算实现这种锁,不过好在通过Redis的相关命令也是可以自己实现锁功能(通过Redis中的key来实现,成功获取锁设置一个key,释放锁删除那个key,通过判断key是否存在来确定是否可以获取锁)。自己实现锁功能需要注意的是锁的释放,比如客户端A获取了库存锁,但是突然宕机了,这样其它客户端就无法获取锁了。所以在自定义锁的时候,可以通过Redis的key过期功能,给锁(一般就是Redis中的key)添加一个过期时间,这样就算客户端宕机了锁也能自动释放。另外需要注意的是锁的过期时间,因为不是所有的操作都是简单的,有些客户端可能需要花很长的时间才能完成事务,如果客户端在事务完成前,锁就自动释放了,这样会造成数据错误。一般解决方法是在锁快要释放之前,重新刷新锁的过期时间,也就是续约锁。自己实现锁还需要考虑很多东西,好在redisson这个开源库实现了Redis锁,可以直接参考或者使用。

三、Redis分布式锁

Redis分布式锁基本上就是上面所说的自定义实现的锁,因为Redis可以被多个应用访问,所以通过Redis锁就可以让多个应用通过锁来访问资源。但是一般的Redis分布式锁都存在一个问题,就是对于故障转移的能力不够。比如Redis主服务A和从服务A1组成了一个Redis服务,客户端C申请了锁,但是在A向A1同步数据前(相对客户端来说A和A1之间同步数据是异步的)宕机了,然后根据故障转移规则A1升级成了主服务,但是这时A1是没有C所申请的锁的信息,如果这时其它客户端申请相同的锁,那么它就能获取锁,此时两个客户端都拥有了相同的锁。
针对这种情况Redis官方开发了一种叫Redlock的分布式锁,相对于前面所说的实现(单台Redis主服务),Redlock使用多台Redis主服务的方式来实现。简单来说如果有N台主服务,那么在指定时间范围内获取到N/2+1台主服务中的锁,那么就获取锁成功。也就是如果现在有5台主服务,那么至少要在3台主服务中获取到锁,Redlock才算获取成功。这么做主要是为了防止某一台服务宕机后,其它服务还能正常运行。
Redlock算法原理可以参考https://redis.io/topics/distlock
redisson这个开源库实现了Redlock

四、Redis分片

Redis实现了主从服务,但是这种方式只是扩展了读请求,对于写请求依然是单服务。为了扩展写请求需要一组相互独立Redis主服务,并且客户端需要自行将数据分片到不同的Redis主服务中。Redis分片的原理基本上就是crc32(key) mod N,其中N表示主服务的个数。
分片在不同软件层次中实现:
1、客户端分片,由客户端实现将数据分片到哪个服务中
2、代理分片,由代理决定将数据分片到哪个服务中
3、查询路由,客户端随机访问一台服务,由服务决定是自己处理还是路由到其它服务器。Redis Cluster实现了混合形式的查询路由,也就是在查询路由的基础上,添加一个缓存表,这张表会逐步记录key与服务器之间的关系。比如客户端首先将key1发往服务器A,然后服务器A发现key1应该由服务器B处理,它会将这个路由信息告诉客户端,客户端缓存表会记录key1由服务器B处理,当第二次查询key1的时候,客户端会直接请求服务器B。
分片实现方案:
1、一致性哈希算法,这个算法在分布式应用中比较广泛,可以动态扩容和缩容。但是这个算法不太适合将Redis用来存储持久化数据,主要是因为这个算法没有数据迁移功能。比如原本key1存储在服务器A,扩容后key1需要存储在服务器B,这时key1的数据在逻辑上已经丢失了。这个算法最适合将Redis用来缓存数据,第一个是因为这个算法在扩容和缩容时只会影响一部分key的映射,第二个受影响的key就算丢失了数据也不会对应用造成影响,第三个通过对key设置过期时间,那些受影响的key的老数据会自动删除。
2、预分片,这个方案比较适合将Redis用来存储持久化数据。预分片的原理就是提前预测好未来需要多少台Redis服务。假设未来最多需要10台服务器,那么在一开始的时候就在一台服务器上开启10个Redis服务。等到内存不够的时候,准备一台新的服务器,将5个Redis服务迁移到新的服务器中,通过Redis的数据备份、主从复制等功能这个方案完全可以实现。一致性哈希算法也可以用作预分片的哈希算法,只要限制它扩容或缩容。
分片缺点:
1、涉及多个key的操作通常不允许,比如集合的交集、差集等操作。主要是分片后多个key不在同一服务中,分片之间也不可能移动数据。
2、同时操作多个key,则不能使用Redis事务。
3、数据备份会比较复杂,因为现在数据分散在各个分片中。
4、不太支持动态扩容或缩容,只能在一定条件下使用。

四、Redis Cluster

Redis Cluster是官方实现的一种分片方案。它没有使用一致性哈希算法,而是使用了一种叫哈希槽的概念。Redis Cluster一共有16384个哈希槽,然后使用CRC16(key) mod 16384来计算每个key所对应的哈希槽。Redis Cluster中的每个节点(redis服务)都会负责维护一部分哈希槽,比如你的集群中有3个节点,那么:

  • Node A contains hash slots from 0 to 5500.
  • Node B contains hash slots from 5501 to 11000.
  • Node C contains hash slots from 11001 to 16383.

假如你通过CRC16(key) mod 16384算出来的结果是5501,那么当你第一次查询的时候客户端会随机访问一个节点,比如Node A,它会首先查看5501是否由自己负责,如果是就直接处理,如果不是就告诉客户端Node B负责处理,客户端需要重新向Node B发送请求。
Redis Cluster相比之前的分片方案,它具有高扩展性和伸缩性,一致性哈希算法也可以扩展和伸缩但是它无法迁移数据。比如现在有A、B、C三个节点,你需要添加D,那么首先将D添加到集群中,然后从A、B、C中分别迁移一部分哈希槽到D(哈希槽所关联的数据会一同转移)。如果要删除节点A,首先需要将A负责的哈希槽移动到B 、C,然后就可以关闭节点A。
Redis Cluster允许你在一定范围内执行涉及多个key的操作,包括交集、差集、事务等操作,只要你所操作的key都关联到同一哈希槽。这个可以通过hash tags的技术强制将不同的key映射到同一哈希槽。比如hello{word}、say{word}、test{word}这3个key用来计算哈希槽的部分不是整体,而是包围在花括号中的word,这样它们所关联的哈希槽就是同一个。
Redis Cluster使用主从模式来备份数据和提供可靠性,假设有A、B、C三个主节点,以及A1、B1、C1三个从节点,那么每个节点都有一个备份从节点,如果A节点宕机了,那么A1会被推选为新的主节点替换A节点。当然如果A1也宕机了,那么集群将停止工作。
Redis Cluster无法保证数据的强一致性,也就是说在一些条件下集群已经对客户端确认的写入依然会丢失。假设有A、B、C三个主节点,以及A1、B1、C1三个从节点,A接收写入并且向客户端确认,在A向A1同步数据前宕机了,然后A1被推选为新的主节点替换A,那么刚刚的写入数据将丢失。类似的情况也会发生在网络分区阶段,主节点少部分的分区一般也会丢失一部分数据。不过集群对数据丢失会限制在一定时间窗口内。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,504评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,434评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,089评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,378评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,472评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,506评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,519评论 3 413
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,292评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,738评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,022评论 2 329
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,194评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,873评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,536评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,162评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,413评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,075评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,080评论 2 352

推荐阅读更多精彩内容