redis
数据保存在内存中,读写操作只要访问内存,不需要磁盘IO
。
redis
的数据以key:value
的格式存储在散列表中,时间复杂度o(1)
。redis
为value
定义了丰富的数据结构,包括动态字符串、双向链表、压缩列表、hash
、跳表和整数数组,可以根据value的特性选择选择最高效的数据结构。redis
的网络IO
和数据读写使用单线程模型,可以绑定CPU
,这避免了线程上下文切换带来的开销。
「注意:redis6.0对网络请求引入了多线程模型,读写操作还是用单线程。」
redis
多线程网络模型见下图:
redis
采用epoll
网络模型,如下图:
内核会一直监听新的socket
连接事件的和已建立socket
连接的读写事件,把监听到的事件放到事件队列,redis使用单线程不停的处理这个事件队列。这避免了阻塞等待连接和读写事件到来。
这些事件绑定了回调函数,会调用redis的处理函数进行处理。
redis
有5种数据类型,包括「字符串、列表、集合、有序集合和字典」。
redis
底层的数据结构有6种,包括「动态字符串、双向链表、压缩列表(ziplist)、hash表、跳表(skip list)和整数数组」。
redis
数据类型和底层数据结构有如下对应关系:
底层数据结构是动态字符串。
如果同时满足下面条件,就使用压缩列表,否则使用双向链表。
64
字节512
「压缩列表」在内存中是一块儿连续的内存空间,结构如下:
「压缩列表查找时间复杂度是o(n)
」
如果同时满足下面条件,就使用有序整数数组,否则使用hash表。
512
个如果同时满足下面2个条件,就使用压缩列表,否则使用跳表。
64
字节128
个「注意:有序集合还有一个HASH
表用于保存集合中元素的分数,做ZSCORE
操作时,查询的就是这个HASH
表,所以效率很高。」
「跳表」的结构如下:
如果不加索引,查找10
这个数字需要查询10
次,使用了二级索引,查找10
这个数字需要5
次,而使用一级索引,需要查询3
次。
❝跳表的每一层都是一个有序链表,最下面一层保存了全部数据。跳表插入、删除、查询的时间复杂度是
o(logN)
。跳表需要存储额外的索引节点,会增加额外的空间开销。❞
如果同时满足下面2个条件,就使用压缩列表,否则使用hash表。
entry
的key/value
都小于64字节512
个redis
总共有8
种淘汰策略,如下图:
volatile-lfu
和allkeys-lfu
策略是4.0
版本新增的。
redis
持久化的方式有2
种,一种是写后日志(AOF)
,一种是内存快照(RDB)
。
AOF
日志记录了每一条收到的命令,redis
故障宕机恢复时,可以加载AOF
日志中的命令进行重放来进行故障恢复。AOF
有3
种同步策略,如下图:
❝如果不是对丢失数据特别敏感的业务,推荐使用
everysec
,对主线程的阻塞少,故障后丢失数据只有1s
。❞
RDB
快照是一个内存快照,记录了redis
某一时刻的全部数据。
从redis4.0
开始,AOF
文件也可以保存RDB
快照,AOF
重写的时候redis
会把AOF
文件内容清空,先记录一份RDB
快照,这份数据以"REDIS"
开头。记录RDB
内容后,AOF
文件会接着记录AOF
命令。故障恢复时,先加载AOF
文件中RDB
快照,然后回放AOF
文件中后面的命令。
redis
主从同步时,主节点会先生成一份RDB
快照发送给从节点,把快照之后的命令写入主从同步缓存区(replication buffer)
,从节点把RDB
文件加载完成后,主节点把缓存区命令发送给从节点。
AOF
日志是用记录命令的方式追加的,这样可能存在对同一个key
的多条命令,这些命令是可以合并成1
条的。比如对同一个key
的多个set
操作日志,可以合成一条。
AOF
重写和RDB
快照执行的过程中,redis
都会fork
一个子进程来执行操作,子进程执行过程中是不是阻塞主线程的。
「但是要注意2
点:」
fork
子进程的过程中,redis
主线程会拷贝一份内存页表(记录了虚拟内存和物理内存的映射关系)给子进程,这个过程是阻塞的,redis
主线程内存越大,阻塞时间越长;redis
主线程共用一块儿物理内存,如果新的请求到来,必须使用copy on write
的方式,拷贝要修改的数据页到新的内存空间进行修改。如下图:❝注意:如果开启了内存大页,每次拷贝都需要分配2MB的内存。
❞
下图是一个「一主二从三哨兵」的架构图:
从图我们可以看到哨兵之间、哨兵和主从节点之间、哨兵和客户端之间都建立了连接。
如果主节点挂了,哨兵集群需要完成主从切换,如下图:
下面我们依次来聊一下这4个步骤「5.1~5.4」。
当一个哨兵监控到主节点下线时,就会给其他哨兵发送确认命令,其他命令会根据自己的判断回复"Y"
或"N"
。
如果有n/2 + 1
以上数量的哨兵都认为主节点下线了,才会判定主节点下线。这里的n
是哨兵集群的数量。
❝
n/2 + 1
这个参数由quorum
参数配置,比如有5
个哨兵,这里一般配置成3
。也可以配置成其他值。❞
主节点被判定下线后,哨兵集群会重新选择新的主节点。
根据配置参数down-after-milliseconds * 10
来淘汰。
「down-after-milliseconds」表示主从节点断开时间,10
表示次数,如果从节点跟主节点断开时间超过down-after-milliseconds
的次数达到了10
次以上,从节点就被淘汰了。
「slave-priority」参数配置了从节点的优先级,选择从节点时哨兵会优先选择优先级高的从节点。
redis
有一个记录主从增量复制的缓存区叫repl_backlog_buffer
,这是一个环形结构的缓冲区,如下图:
主节点有一个写偏移量master_repl_offset
,从节点也有一个偏移量slave_repl_offset
。优先选择slave_repl_offset
最接近master_repl_offset
的从节点作为新的主节点。
所以,上图中偏移量为114
的从节点优先被选为新的主节点。
优先级和参数都一样的情况下,ID
编号小的从节点优先被选为新主节点。
第一个判断主节点下线的哨兵节点收到其他节点的回复并确定主节点下线后,就会给其他哨兵发送命令申请成为哨兵leader
。
「成为leader的条件如下:」
quorum
值❝如果集群配置了5个哨兵,
quorum
的值设置为3,其中一个哨兵节点挂了,很有可能会判断到主节点下线,但是因为选举不出哨兵leader
而不能切换。如果集群有2
个哨兵,其中一个挂了,那必定选不出哨兵leader
。❞
下面的图展示了哨兵一成功当选leader
的过程:
选出新主节点和哨兵leader
后,哨兵leader
会执行主从切换的操作。完成后会做一些「事件通知」:
如果客户端的读请求会发送到从节点,可以正常处理。
在客户端收到新主节点地址通知前写请求会失败。
客户端可以采取一些应急措施应对主节点下线,比如缓存写请求。
为了能够及时获取到新主节点信息,客户端可以订阅哨兵的主节点下线事件和新主节点变更事件。
redis
变慢了的原因有很多,总结一下有11
个,见下图:
从图中看出,redis
变慢原因主要有两类:「阻塞主线程和操作系统限制」。
前面已经讲过了,redis
在AOF
重写时,主线程会fork
出一个bgrewriteaof
子进程。
redis
进行RDB
快照时主线程会fork
出一个bgsave
子进程。
这两个操作表面上看不阻塞主线程,但fork
子进程的这个过程是在主线程完成的。fork
子进程时redis
需要拷贝内存页表,如果redis
实例很大,这个拷贝会耗费大量的CPU
资源,阻塞主线程的时间也会变长。
redis
默认支持内存大页是2MB
,使用内存大页,一定程度上可以减少redis
的内存分配次数,但是对数据持久化会有一定影响。
redis
在AOF
重写和RDB
快照过程中,如果主线程收到新的写请求,就需要CopyOnWrite
。使用了内存大页,即使redis
只修改其中一个大小是1kb
的key
,也需要拷贝一整页的数据,即2MB
。在写入量较多时,大量拷贝就会导致redis
性能下降。
执行复杂度高的命令是造成redis
阻塞的常见原因。比如对一个set
或者list
数据类型执行SORT
操作,复杂度是O(N+M*log(M))
。
如果一个key
的value
非常大,创建的时候分配内存会很耗时,删除的时候释放内存也很耗时。
redis4.0
以后引入了layfree
机制,可以使用子进程异步删除,从而不影响主线程执行。用UNLINK
命令替代DEL
命令,就可以使用子进程异步删除。
redis6.0
增加了配置项lazyfree-lazy-user-del
,配置成yes
后,del
命令也可以用子进程异步删除。
❝如果
lazyfree-lazy-user-del
不设置为yes
,那redis
是否采用异步删除,是要看删除的时机的。对于String
类型和底层采用整数数组和压缩列表的数据类型,redis
是不会采用异步删除的。❞
从节点全量同步过程中,需要先清除内存中的数据,然后再加载RDB文件,这个过程中是阻塞的,如果有读请求到来,只能等到加载RDB文件完成后才能处理请求,所以响应会很慢。
另外,如果redis
实例很大,也会造成RDB
文件太大,从库加载时间长。所以尽量保持redis
实例不要太大,比如单个实例限制4G
,如果超出就采用切片集群。
appendfsync
策略有3种:always、everysec、no
,如果采用always,每个命令都会同步写盘,这个过程是阻塞的,等写盘成功后才能处理下一条命令。
除非是严格不能丢数据的场景,否则尽量不要选择always
策略,推荐尽量选择everysec
策略,如果对丢失数据不敏感,可以采用no
。
内存达到maxmemory
,需要使用淘汰策略来淘汰部分key
。即使采用lazyfree
异步删除,选择key
的过程也是阻塞的。
❝可以选择较快的淘汰策略,比如用随机淘汰来替换
LRU
和LFU
算法淘汰。也可以扩大切片数量来减轻淘汰key
的时间消耗。❞
使用swap
的原因是操作系统不能给redis
分配足够大的内存,如果操作其他开启了swap
,内存数据就需要不停地跟swap
换入和换出,对性能影响非常大。
操作系统没有能力分配内存的原因也可能是其他进程使用了大量的内存。
如果网卡负载很大,对redis
性能影响会很大。这一方面有可能redis
的访问量确实很高,另一方面也可能是有其他流量大的程序占用了带宽。
这个最好从运维层面进行监控。
redis
虽然是单线程的,但是在多核cpu
的情况下,也可能会发生上下文切换。如果主线程从一个物理核切换到了另一个物理核,那就不能使用CPU
高效的一级缓存和二级缓存了。如下图所示:
为防止这种情况,可以把redis
绑定到一个CPU
物理核。
对于AOF
同步写盘的使用场景,如果磁盘性能低,也会影响redis
的响应。可以优先采用性能更好的SSD
硬盘。
redis
的zset
类型保存了分数值,可以方便的实现排行榜的功能。
比如要统计10
篇文章的排行榜,可以先建立一个存放10
篇文章的zset
,每当有读者阅读一篇文章时,就用ZINCRBY
命令给这篇文章的分数加1
,最后可以用range
命令统计排行榜前几位的文章。
如下图,一个服务部署了2
个客户端,获取分布式锁时一个成功,另一个就失败了。
redis
一般使用setnx
实现分布式锁,命令如下:
SETNX KEY_NAME VALUE
设置成功返回 1
,设置失败返回 0
。
使用单节点分布式锁存在一些问题。
结果锁就不能释放了,其他客户端永远获取不到锁。解决方法是用下面命令对key
设置过期时间:
SET key value [EX seconds] [PX milliseconds] NX
解决方法是对key
设置value
时加入一个客户端表示,比如在客户端1
设置key
时在value
前拼接一个字符串application1
,删除的时候做一下判断。
redis
单节点会有可靠性问题,节点故障后锁操作就会失败。redis
为了应对单点故障的问题,设计了多节点的分布式锁,也叫红锁。主要思想是客户端跟多个redis
实例请求加锁,只有超过半数的实例加锁成功,才认为成功获取了分布式锁。
如下图,客户端分别跟3个实例请求加锁,有2个实例加锁成功,所以获取分布式锁成功:
redis
做缓存时,如果同一时间大量缓存数据失效,客户端请求会大量发送到数据库,导致数据库压力激增。如下图:
「应对方法主要有3个:」
某个热点key
,突然过期了,大量请求发送到了数据库。解决方案是给热点key
不设置过期时间。
某个热点key
,查询缓存和查询数据库都没有,就发生了缓存穿透。如下图:
「应对方法主要有2个:」
什么是数据倾斜?看下面这个面试题:
❝如果
redis
有一个热点key
,qps
能达到100w
,该如何存储?❞
如果这个热点key
被放到一个redis
实例上,这个实例面临的访问压力会非常大。如下图,redis3
这个实例保存了foo
这个热点key
,访问压力会很大:
「解决方法主要有两个:」
1.使用客户端本地缓存来缓存key
,这样改造会有两个问题:
key
可能消耗大量内存redis
缓存的一致性2.给热点key
加一个随机前缀,让它保存到不同的redis
实例上,这样也会存在两个问题:
key
加前缀key
❝有一道经典的面试题,
10
亿整数怎么在内存中去重排序?❞
我们先算一下10
亿整数占的内存,java
一个整数类型占四字节,占用内存大小约
10亿 * 4 / 1024 / 1024 = 3.7G
占得内存太大了,如果内存不够,怎么办呢?
bitmap
类型使用的数据结构是String
,底层存储格式是二进制的bit
数组。假如我们有1、4、6、9
四个数,保存在bit
数组中如下图:
在这个bit
数组中用10
个bit
的空间保存了四个整数,占用空间非常小。
再回到面试题,我们使用bit
数组长度是10
亿整数中 「(最大值 - 最小值 + 1)」。
❝如果有负数,需要进行一个转化,所有数字加最小负数的绝对值。比如
{-2, 0, 1, 3}
,我们转换成{0, 2, 3, 5}
,因为数组下标必须从0开始❞
在一个有100
个员工的公司,要统计一个月内员工全勤的人数,可以每天创建一个bitmap
,签到的员工bit
位置为1
。
要统计当天签到的员工只要用BITCOUNT
命令就可以。
要统计当月全勤的员工,只要对当月每天的bitmap
做交集运算就可以,命令如下:
BITOP AND srckey1 srckey2 srckey3 ... srckey30
❝
srckeyN
表示第N
天的打卡记录bitmap
❞
比如网站有10
万个用户,这样我们创建一个长度为10
万的bitmap
,每个用户id
占一个位,如果用户登录,就把bit
位置为1
,日终的时候用BITCOUNT
命令统计出当天登录过的用户总数。
本文由哈喽比特于3年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/9n8RrAj4l1DvO38D31QamQ
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。