赞
踩
多路I/O复用模型、VM机制、文件事件处理器
对于非实时变化的数据,查询mysql耗时需要300ms,存到缓存redis,每次查询仅仅1ms,性能瞬间提升百倍。
mysql 单机支撑到2K QPS就容易报警了,如果系统中高峰时期1s请求1万,仅单机mysql是支撑不了的,但是使用缓存的话,单机支撑的并发量轻松1s几万~十几万。
原因是缓存位于内存,内存对高并发的良好支持
应用场景:存对象(String和Hash的比较)
字符串对象是使用最广泛的类型。也是redis其他数据类型嵌套使用的对象类型。有三种编码方式:int、raw、embstr三种。String是最常用的一种数据类型,普通的key/value存储都可以归为此类, 不是java语言中的String,可以是字符串、整数或者浮点数。
常规key-value缓存应用。常规计数: 微博数, 粉丝数(用incr,decr)
一个键值对集合。key是键,value相当于是一个map(String 类型的 field 和 value 的映射表),hash 特别适合用于存储对象
key是对象的ID,value是对象json序列化后的结果
以string的方式存储对象的缺点:增加了序列化/反序列化的开销,在需要修改其中一项信息时,需要把整个对象取回,并且修改操作需要对并发进行保护,引入CAS等复杂问题
key是对象的ID,value是一个Map(这个Map的key是成员的属性名,value是属性值)
以Hash存储对象的优点:既不需要重复存储数据,也不会带来序列化和并发修改控制的问题,很好的解决了问题
List类型是按照插入顺序排序的字符串链表(类似java中的LinkedList),和数据结构中的普通链表一样,我们可以在其头部(left)和尾部(right)添加新的元素。在插入时,如果该键并不存在,Redis将为该键创建一个新的链表。与此相反,如果链表中所有的元素均被移除,那么该键也将会被从数据库中删除。
做消息队列(lpop和rpush)(详情见上文《如何使用redis做消息队列》)
redis中的set就是常见的无序集合(通过hashtable实现的),特点就是包含的每个字符串都是独一无二各不相同的
常用操作:添加、获取、移除单个元素;检查一个元素是否存在于集合中,计算交集、并集、差集(这个是亮点,不然和Java的HashSet差不多)。从集合中随机获取元素
Redis set对外提供的功能与list类似是一个列表的功能,特殊之处在于set是可以自动排重的,当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择,并且set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list所不能提供的。
在分布式篇中的接口幂等性设计中,用set类型来存放请求的唯一识别,存进去代表可以调用请求
在微博中,可以将一个用户所有的关注人存在一个集合中,将其所有粉丝存在一个集合(自动去重)。Redis还为集合提供了求交集、并集、差集等操作,可以非常方便的实现如共同关注、共同喜好、二度好友等功能,对上面的所有集合操作,你还可以使用不同的命令选择将结果返回给客户端还是存集到一个新的集合中
Redis sorted set的内部使用HashMap和跳跃表(SkipList)来保证数据的存储和有序,HashMap里放的是成员到score(优先级)的映射,而跳跃表里存放的是所有的成员,排序依据是HashMap里存的score,使用跳跃表的结构可以获得比较高的查找效率(跳跃表的优点),并且在实现上比较简单。
Redis sorted set的使用场景与set类似,区别是set不是自动有序的,而sorted set可以通过用户额外提供一个优先级(score)的参数来为成员排序,并且是插入有序的,即自动排序。当你需要一个有序的并且不重复的集合列表,那么可以选择sorted set数据结构,
比如twitter 的public timeline可以以发表时间作为score来存储,这样获取时就是自动按时间排好序的。
和Set相比,Sorted Set关联了一个double类型权重参数score,使得集合中的元素能够按score进行有序排列,redis正是通过分数来为集合中的成员进行从小到大的排序。zset的成员是唯一的,但分数(score)却可以重复。
比如一个存储全班同学成绩的Sorted Set,其集合value可以是同学的学号,而score就可以是其考试得分,这样在数据插入集合的时候,就已经进行了天然的排序。
另外还可以用Sorted Set来做带权重的队列,比如普通消息的score为1,重要消息的score为2,然后工作线程可以选择按score的倒序来获取工作任务。让重要的任务优先执行。
读写性能优秀:数据直接存放在内存里的
支持数据持久化,支持AOF和RDB两种持久化方式
支持主从复制,主机可以自动将数据复制到从机上,可以实现读写分离
数据结构丰富,支持多种数据的存储,如:字符串String、散列Hash、列表List、集合set、有序集合zset等
绝大部分请求是纯粹的内存操作,非常快速。数据存在内存中
对数据操作也简单,Redis中的数据结构是专门进行设计的
避免了CPU的上下文切换、资源竞争问题,不存在加锁释放锁操作,也没有死锁
非阻塞IO(理解为监控室,忘了再百度以下)
select、poll、epoll等,这些函数都可以同时监视多个描述符的读写就绪状况
图片来源:https://blog.51cto.com/u_12874079/2149260
利用系统调用函数 ,使多个描述符的I/O操作都能在一个线程内并发交替地顺序完成(理解为监控室可以看到多个摄像头的视频,做统一监控)
这里“多路”指的是多个网络连接,“复用”指的是复用同一个线程(单线程),redis同时对多个IO进行监控
参考相关链接:
什么是多路I/O复用模型
redis的多路复用是什么鬼
使用VM机制作为底层模型,它们之间底层实现方式以及与客户端之间通信的应用协议不一样,Redis直接自己构建了VM机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求
白话文:VM机制就是可以实现冷热数据分离。使热数据仍在内存中,冷数据保存到磁盘。
Redis 为了达到最快的读写速度将数据都读到内存中,并通过异步的方式将数据写入磁盘。所以 redis 具有快速和数据持久化的特征。如果不将数据放在内存中,磁盘 I/O 速度为严重影响 redis 的性能
不会,Redis使用到了VM机制,在redis.conf设置vm-enabled yes 即开启VM功能。 通过VM功能可以实现冷热数据分离。使热数据仍在内存中,冷数据保存到磁盘。这样就可以避免因为内存不足而造成访问速度下降的问题。
需要注意的是只有热点数据,缓存才有价值对于冷数据而言,大部分数据可能还没有再次访问到就已经被挤出内存,不仅占用内存,而且价值不大。数据更新前至少读取两次,缓存才有意义。这个是最基本的策略,如果缓存还没有起作用就失效了,那就没有太大价值了。
网络 IO 的读写在 Redis 整个执行期间占用了大部分的 CPU 时间,如果把网络处理这部分做成多线程处理方式,那对整个 Redis 的性能会有很大的提升。
白话文:为了加快网络IO操作
不是的,多线程部分只是用来处理网络数据的读写和协议解析,执行命令仍然是单线程
在redis.conf文件里配置以下2个参数(一个是开关、一个是线程数目),可以开启:
io-threads-do-reads yes
io-threads 线程数
上面的线程数一般要少于CPU的核数,如果是8核CPU,建议配置6个线程(官方建议的)
咋看怎么有点像是多路IO复用模型的功能呀?TODO
图片来源:https://blog.csdn.net/weixin_39098944/article/details/107869323
白话文:1. 主线程阻塞,等多线程接收IO完毕,并放到队列后,主线程才开始运行 2. 还是用单线程的方式去处理命令,不存在并发问题
redis 内部使用文件事件处理器(file event handler),这个文件事件处理器是单线程的,所以 redis 才叫做单线程的模型。
它采用 IO 多路复用机制同时监听多个 socket,根据 socket 上的事件来选择对应的事件处理器进行处理。
上图就是,客户端与 redis 的一次通信过程:
原文地址:https://www.jianshu.com/p/8f2fb61097b8
关键字:save、besave、dump.rdb文件小、恢复快、全量备份、丢数据多
客户端向Redis发送save命令来创建一个快照文件
save方式会使redis处于**阻塞状态,**直到RDB完成,才会继续响应其它的命令,对redis性能影响非常大
redis.conf配置文件默认的快照配置,如下
save 900 1
save 300 10
save 60 10000
900 1 表示900秒内如果有1个key发生了变化,那么触发RDB。300 10以此类推。这个是可以自己改的
fork⼀个⼦进程将内存中的数据集快照写⼊磁盘
先写⼊一个临时⽂件,写⼊成功后,再替换之前的⽂件(用二进制压缩存储)
白话文:这个有点像CopyOnWriteArrayList的add操作写时复制的思想
正常退出redis
只包含⼀个经过压缩的二进制文件dump.rdb,占用空间很小,方便持久化。
相对于数据集⼤时,比AOF的启动效率更高
性能最大化,主进程不会进⾏任何 IO 操作,保证了 redis 的高性能 .
保存了 Redis 某个时间点的数据集,很适合用于做备份
如果持久化的时候redis发生故障,会丢失最后一次持久化的数据,所以这种方式更适合数据要求不高的时候
关键字:日志、增量备份、rewrite模式、启动慢、丢数据少
以日志的形式记录服务器所处理的每⼀个写、删除操作,查询操作不会记录,以文本的方式记录,不断追加,可以打开⽂件看到详细的操作记录
每秒同步,异步完成,先将操作写入一个缓存(复制积压缓冲区?),每秒从缓存同步到磁盘一次,效率高,但是还是可能丢失数据
白话文:每秒从复制积压缓冲区同步一次到磁盘
每修改同步,每一次发生增删改都记录操作,数据安全性高,最多丢失一个操作
白话文:很悲观,每次写操作都会同步。。。
不同步,把操作写入缓存,由操作系统来决定什么时候写入磁盘
默认的fsync策略everysec,就算发生机器炸了,也最多只会丢失一秒钟的数据
定期对AOF文件进行重写(很重要,要记住),以达到压缩的目的
比如有先有一个set age 21,后面又有一个set age 22,则前面哪个set重写后会被清楚
相同的数据集,AOF 文件的大小一般会比 RDB 文件大,且恢复速度慢
数据集⼤的时候,比rdb 启动效率低。
原文链接:https://www.jianshu.com/p/5b6ba8942b82
对于数据安全性比较高的,要开启AOF,但是对应的启动速度就慢了
Redis在2.8版本提供了PSYNC命令来带代替SYNC命令
PSYNC < runid> < offset>
runid:主服务器ID
offset:从服务器最后接收命令的偏移量
每个redis节点启动都会生成一个唯一的uuid,每次redis重启之后,runid会变化
主从节点都会维护各自的复制偏移量offset,当主节点有写入命令时,offset=offset+命令的字节长度。从节点接收到主节点的命令后,也会增加自己的offset,并把自己的offset发送给主节点。主节点同时保存自己的和从节点的offset(通过比较判断数据是否一致)
白话文:主节点发送同步操作,并会把offset一起发给从机,从机收到后,需要改下自己的offset,以保持和主机的一致
repl_backlog_size(英文有个印象就行),保存在主节点上的固定长度的先进先出队列(默认1MB),缓存已经传播出去的命令
作用:缓存主机发出去的命令(可以认为是增量数据)
图片相关的视频来源:https://www.zhihu.com/zvideo/1411432217536659456
白话文:全量同步主要操作的是RDB文件
主节点每执行一个写命令就会向从节点发送相同的写命令,从节点接收并执行收到的写命令
主从刚刚连接的时候,进行全量同步;全同步结束后,进行增量同步。当然,如果有需要,slave 在任何时候都可以发起全量同步。redis 策略是,无论如何,首先会尝试进行增量同步,如不成功,要求从机进行全量同步。
视情况而定,主服务器在给从服务发生同步数据的时候,还会把数据发一份到自己的复制积压缓冲区,但是这个复制积压缓冲区容量很小,得看从机未同步的数据在不在复制积压缓冲区
图片来源:https://www.cnblogs.com/zwwhnly/p/12651527.html
关键字:runid、偏移量、复制积压缓冲区、全量同步、增量同步、读写分离
参考上面讲过一个主从同步的原理
主机:支持查询和修改请求
从机:支持查询请求
同一个Master可以同步多个Slave,当然Slave同样可以接受其它Slaves的连接和同步请求
Master机提供读和写的操作,Slave机只提供读操作,从而缓解Master的读操作压力
主机从机的宕机都会导致前端部分读写请求失败,需要等待机器重启或者手动切换前端的IP才能恢复。
主机宕机,有部分数据未能及时同步到从机,切换IP后还会引入数据不一致的问题,降低了系统的可用性。
在集群容量达到上限时在线扩容会变得很复杂。为避免这一问题,运维人员在系统上线时必须确保有足够的空间,这对资源造成了很大的浪费。
关键字:监听、心跳、主观下线、客观下线、自动故障转移
在主从复制模式的基础上,再加一个哨兵集群
哨兵隔一定时间给Master和Slaves、其他Sentinel发送心跳,检测是否运作正常。
当被监控的某个Redis节点出现问题时, 哨兵可以通过 API 向管理员或者其他应用程序发送通知。
在Master主服务器发生故障的时候,可以实现Master和Slave服务器的切换(故障迁移)
哨兵会定时对redis集群的所有节点发送心跳,如果一个节点在指定时间内没有回复,则该redis节点被该哨兵节点主观下线
白话文:我叫你,你不应,我就认为你掉线了(但你有没有掉线我不知道,这是我主观认为的)
在哨兵模式中,当多数哨兵判断该节点为主观下线,那么该节点就是客观下线
白话文:大家都认为你掉线了,那你就是真的掉线了(客观事实嘛)
如果客观下线的节点是主节点,那么就要进行自动故障转移
通过选举机制(投票,半数通过就行),将其中一个Slave升级为新的Master,其他的Slave重新指向(slave of)找个新的Master,并告诉客户端这个新的Master地址
Master的redis.conf、Slave的redis.conf的配置文件的内容都会发生相应的改变,slave of指向发生改变
sentinel.conf的监控目标会随之调换
有提醒功能、还有自动故障转移功能
哨兵不是一个,也是一个集群,一个哨兵挂了还有其他的哨兵,且哨兵模式是基于主从模式的,所有主从的优点,哨兵模式同样具有
又要配置主从模式、又要配置哨兵集群的,头大。。。
关键字:去中心化、
任何两个节点之间都是相互连通的,客户端连接任何一个节点,然后就可以访问集群中的任何一个节点,对其进行存取和其他操作
像上面说的2中模式,客户端都是需要连接Master节点,才能访问其他节点
因为如果集群的话,是有好多个redis一起工作的,那么,就需要这个集群不是那么容易挂掉(保证稳定性),所以呢,理论上就应该给集群中的每个节点至少一个备用的redis服务。这个备用的redis称为从节点(slave)
采用一种ping-pong的交互方式,可以认为是心跳检测机制
节点连接正常
有一下两种情况:
如果集群任意master挂掉,且当前master没有slave
这可是支持在线扩容吼!很牛,区别上面两个模式
一个Redis实例具备了“数据存储”和“路由重定向”,完全去中心化的设计。这带来的好处是部署非常简单,直接部署Redis就行
容易挂掉,且从节点只能复制自己的主节点,不能复制其他节点
很难对业务进行无痛的升级,如果哪天Redis集群出了什么严重的Bug,就只能回滚整个Redis集群。
原文地址:https://www.cnblogs.com/zhuyeshen/p/11737273.html
是一个可以存储两个数值的:
槽是 Redis 集群管理数据的基本单位
一个key过来,进行hash取余运算(hashcode(key)%16384),通过这个值,去找到对应的插槽所对应的节点,从而得知自己要存到哪个节点
比如你有 N 个 redis实例,那么如何将一个key映射到redis上呢,你很可能会采用类似下面的通用方法计算 key的 hash 值,然后均匀的映射到到 N 个 redis上:hash(key)%N
在这两种情况下,几乎所有的缓存都失效了。会导致数据库访问的压力陡增,严重情况,还可能导致数据库宕机。
一致性hash算法主要应用于分布式存储系统中,可以有效地解决分布式存储结构下普通余数Hash算法带来的伸缩性差的问题(不好扩容),可以保证在动态增加和删除节点的情况下尽量有多的请求命中原来的机器节点。
把对象映射到0-2的32次幂减1的空间里。现在假设有4个对象:object1-object4,将四个对象hash后映射到环形空间中
基本思想就是将对象和cache都映射到同一hash数值空间中,并且使用相同的hash算法,可以使用cache的ip地址或者其他因子,假设现在有三个cache
找到的第一个cache节点就是存储位置
移除一个cacheB节点、这时候key4将找不到cache,key4继续使用一致性hash算法运算后算出最新的cacheC,以后存储与读取都将在cacheC上
图片摘自原文链接:https://www.cnblogs.com/kenwar/p/9264856.html
在该节点逆时针计算到遇到的第一个cache节点之间的数据节点。(影响上一个节点)
添加节点逆时针遇到的第一个cache节点之间的数据节点(影响上一个节点)
原文链接:https://www.e-learn.cn/content/redis/2344485
为了使集群能够水平扩展,首要解决的问题就是如何将整个数据集按照一定的规则分配到多个节点上
范围分片,哈希分片,一致性哈希算法和虚拟哈希槽
假设数据集是有序,将顺序相临近的数据放在一起,可以很好的支持遍历操作
存在热点。比如日志类型的写入,一般日志的顺序都是和时间相关的,时间是单调递增的,因此写入的热点永远在最后一个分片
所有的key根据哈希函数映射到 0 ~ 16383 整数槽内,每一个节点负责维护一部分槽以及槽所映射的键值数据
关键命令:meet、importing、migrating、setkeysinslot、migrate(超时、pipeline机制)、node
新节点指定槽的迁移计划,确保迁移后每个节点负责相似数量的槽,从而保证这些节点的数据均匀
指在某一个时间段,缓存集中过期失效。所有原本应该访问缓存的请求都去查询数据库了,而对数据库CPU和内存造成巨大压力,严重的会造成数据库宕机。从而形成一系列连锁反应,造成整个系统崩溃。
大量key同时过期
过期时间+随机数:尽量让缓存失效的时间均匀分布,最次也得随机分布,尤其是一些访问大的接口。
加锁或者队列:防止大量线程对数据库的一次性进行读写,避免缓存失效时对数据库造成的巨大冲击,但吞吐量就降低了。
指查询一个一定不存在的数据。由于缓存不命中,并且出于容错考虑,如果从数据库查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,缓存失去意义。
黑客攻击,查询数据库中不存在的数据
如果数据库返回信息为null,也可以将这个空对象设置到缓存里边去。下次再请求的时候,就可以从缓 存里边获取了
注意:将空对象设置一个较短的过期时间(因为没有意义,存着就是为了防止黑客高频null攻击)
关键字:bitmap数组、hash算法、误判率
将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。由于请求的参数是不合法的(每次都请求不存在的参数),于是我们可以使用布隆过滤器(BloomFilter)提前拦截,不合法就不让这个请求到数据库层。
下面是关于布隆过滤器的介绍:
private static BloomFilter<Integer> bloomFilter = BloomFilter.create(Funnels.integerFunnel(), size,0.01);
一般都是哈希取余运算吧,将数据库中查出来的数据都塞到这个bitmap里,并设置对应的bit字段为1,代表有数据
bitmap的初始容量大小,其内部维护一个全为0的bit数组
误判率越低,要求精确度更高,则数组越长,所占空间越大。误判率越高则数组越小,所占的空间越小。
白话文:要求精确度更高,肯定要牺牲更多的资源,所以要根据业务选择合适误判率
用于快速判读某个元素是否存在于集合中,类似于Java中HashSet的功能
布隆过滤器的关键就在于hash算法和容器大小(容器大小应该和误判率有关)
白话文:知道了上面的相关概念,应该也就知道原理了,不多解释
缓存击穿指的是热点key在某个特殊的场景时间内恰好失效了,恰好有大量并发请求过来了,造成DB压力(屋漏偏逢连夜雨)
其实缓存击穿和缓存雪崩从概念上来讲差不多,只是缓存击穿是某些热点key,而雪崩指的是大规模的key。
热点key过期
对于一些热点key,过期时间可以无限调长
加锁或者队列的方式:和缓存雪崩处理方式意义
指当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然需要保证服务还是可用的,即使是有损服务。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级。
资源紧张,但要保证核心业务可用
让自己不可用,以保证核心服务可用,即使是有损的。而且有些服务是无法降级的(如加入购物车、结算)。
在进行降级之前要对系统进行梳理,看看系统是不是可以丢卒保帅;从而梳理出哪些必须誓死保护,哪些可降级;比如可以参考日志级别设置预案:
一般:比如有些服务偶尔因为网络抖动或者服务正在上线而超时,可以自动降级;
警告:有些服务在一段时间内成功率有波动(如在95~100%之间),可以自动降级或人工降级,并发送告警;
错误:比如可用率低于90%,或者数据库连接池被打爆了,或者访问量突然猛增到系统能承受的最大阀值,此时可以根据情况自动降级或者人工降级;
严重错误:比如因为特殊原因数据错误了,此时需要紧急人工降级。
缓存预热也是一个比较常见的概念,缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题。用户直接查询事先被预热的缓存数据。
除了缓存服务器自带的缓存失效策略之外(Redis默认的有6中策略可供选择),我们还可以根据具体的业务需求进行自定义的缓存淘汰,常见的策略有两种:
Redis使用maxmemory-policy(最大内存策略),即Redis中的数据占用的内存超过设定的最大内存时的操作策略,用的是LRU算法
LRU算法,least RecentlyUsed,最近最少使用算法。也就是说默认删除最近最少使用的键。
redis中并不会准确的删除所有键中最近最少使用的键,而是随机抽取3个键,删除这三个键中最近最少使用的键。那么3这个数字也是可以设置的,对应位置是配置文件中的maxmeory-samples.
Redis的每个库都有一个过期字典,过期字典中保存所有key的过期时间。当客户端读取一个key时会先到过期字典内查询key是否已经过期,如果key已经超过,会执行删除操作并返回空。这种策略是出于节省CPU成本考虑,但是单独用这种方式存在内存泄露的问题,当过期键一直没有访问将无法得到及时删除,从而导致内存不能及时释放。
Redis内部维护一个定时任务,默认每秒运行10次过期扫描(通过 redis.conf 中通过 hz 配置 修改运行次数),扫描并不是遍历过期字典中的所有键,而是采用了自适应算法,根据键的过期比例、使用快慢两种速率模式回收键:
1. 从过期字典中随机取出 20 个键
2. 删除这 20 个键中过期的键
3. 如果过期键的比例超过 25% ,重复步骤 1 和 2
为了保证扫描不会出现循环过度,一直在执行定时删除定时任务无法对外提供服务,导致线程卡死现象,还增加了扫描时间的上限,默认是 25 毫秒(即默认在慢模式下,25毫秒还未执行完,切换为块模式,模式下超时时间为1毫秒且2秒内只能运行1次,当慢模式执行完毕正常退出,会重新切回快模式)
原文链接:Redis 缓存更新策略
tips:我们对缓存的操作应该是删除,而不是更新,由下个请求去去缓存,发现不存在后再读取数据库,写入缓存(重要)
以下的两种操作,都可能涉及主动更新缓存:
先去缓存中查询数据,如果没有,则从数据库中查询,并放到缓存中
按照操作顺序可以分为以下几种:
A请求删除缓存后,还未更新数据库的时候,突然!B请求来了(一次查询操作),查缓存发现没数据,就去数据库查询,B请求把老的数据查询出来,并放到缓存中,之后A请求将数据库更新
缓存的数据是老的,数据库的是新的
大概率事件,因为B请求的查询速度>A请求的更新速度,很可能存在的
小概率情况下可能会存在脏读:缓存刚好失效,A请求直接查询数据库(旧值),突然!来了个B请求(一次更新操作),一气呵成,更新了数据库,且删除了缓存,之后,A请求将查询到的旧值写入缓存
缓存的数据是老的,数据库的是新的,A请求出现了脏读
总结:缓存刚好失效,A查询到旧值还没来得及操作redis,刚好B请求来一波操作(更新数据库、删除缓存)
小概率事件?因为读速度远大于写速度,B请求很难做到一气呵成,所以一般做到这个情况就可以了
先删缓存,再更新数据库,延迟1s,再删缓存
延迟多久怎么判断:确保确保读请求结束,写请求可以删除读请求造成的缓存脏数据
优秀的文章:缓存和数据库一致性问题,看这篇就够了
使用list类型作为消息队列,生产者负责生产待处理的消息,消费者监视list队列并负责处理消息。
业务过程实现先进先出(FIFO)。客户端总是要不断监控新的消息(不断去判断队列中还有没有消息,浪费资源),因此需要使用BLPOP命令——lpop的阻塞版本。同时我们在while循环中调用blpop命令,模拟一直在监控。
个人理解:有点类似BlockingQueue中的take操作(队列中没有元素的时候会阻塞)
关系型数据中的事务都是原子性的,而Redis 的事务是非原子性的
严格的说Redis的命令是原子性的,而事务是非原子性的,我们要让Redis事务完全具有事务回滚的能力,需要借助于命令WATCH来实现。
tips:redis也可以通过Lua表达式实现原子性操作
redis会将后续的命令逐个放入队列中,然后使用EXEC命令来原子化执行这个命令系列
执行事务中的所有操作命令
放弃执行事务块中的所有命令
监视一个或多个key,如果事务在执行前,这个key(或多个key)被其他命令修改,则事务被中断,不会执行事务中的任何命令
取消WATCH对所有key的监视
原文链接:https://www.cnblogs.com/fengguozhong/p/12161363.html
Redis事务没有回滚操作,是直接中断操作
因为多数事务失败是由语法错误或者数据结构类型错误导致的,语法错误是在命令入队前就进行检测的,而类型错误是在执行时检测的,Redis为提升性能而采用这种简单的事务,这是不同于关系型数据库的,特别要注意区分。
如粉丝列表
如按天存储某项功能或者网站的用户集合
如全表的字段都缓存,将数据从数据库都加载出来序列化放到Redis里,这个方式非常常用
由于Redis单线程的特性,操作bigkey的通常比较耗时,也就意味着阻塞Redis可能性越大,这样会造成客户端阻塞或者引起故障切换,它们通常出现在慢查询中。
有个bigkey,不怎么用到,不怎么查询,当它过期时间到了,因为过期删除的惰性策略,还会一直存在内存中,占用内存
这样会不利于集群对内存的统一管理,存在丢失数据的隐患。
bigkey也就意味着每次获取要产生的网络流量较大,假设一个bigkey为1MB,客户端每秒访问量为1000,那么每秒产生1000MB的流量,对于普通的千兆网卡(按照字节算是128MB/s)的服务器来说简直是灭顶之灾,而且一般服务器会采用单机多实例的方式来部署,也就是说一个bigkey可能会对其他实例造成影响,其后果不堪设想。
当需要对bigkey进行迁移(例如Redis cluster的迁移slot),实际上是通过migrate命令来完成的,migrate实际上是通过dump + restore + del三个命令组合成原子命令完成,如果是bigkey,可能会使迁移失败,而且较慢的migrate会阻塞Redis。
多个指令之间没有依赖关系,可以使用 pipeline 一次性执行多个指令,减少 IO,缩减时间。
使用 redis-benchmark 进行压测的时候可以发现影响 redis 的 QPS峰值的一个重要因素是 pipeline 批次指令的数目。
多个指令之间要求没有互不相干,不存在依赖,才能用
mset, mget是原子性,pipeline不是原子性
如果这个redis正在给线上的业务提供服务,那使用keys指令会有什么问题?
这个时候你要回答redis关键的一个特性:redis的单线程的。keys指令会导致线程阻塞一段时间,线上服务会停顿,直到指令执行完毕,服务才能恢复。
缓存尽可能用在高频访问且时效性要求不高的热点业务上
原文地址:https://www.cnblogs.com/dinglang/p/6117309.html
不同的业务使用不同的redis集群,或者协议使用redis的不同db
多用冒号,命名可以包括业务标识、模块名称等等
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。