Redis应用

开发者 2024-9-27 15:51:14 77 0 来自 中国
1.分布式锁

RedissonLock分布式锁
1)加锁是通过Lua脚本实现的,如果分布式锁不存在,则会通过hset 分布式锁 uuid+threadid标识 1举行加锁,并设超时时间30s
2)如果是重入加锁,则将加锁次数+1
3)如果加锁失败,则返回该锁到逾期还要多长时间ttl,然后再实行加锁,末了利用信号量指定等待ttl时间
4)加锁乐成后,会有一个续命逻辑,每隔10s重新设置超时时间为30s
5)对于加锁失败的客户端,会通过Redis发布订阅机制,订阅分布式锁的Channel。分布式锁开释锁的时间,会发布唤醒这些客户端让其重新去获取分布式锁。
2.缓存

2.1 缓存穿透

缓存穿透是指查询一个根本不存在的数据, 缓存层和存储层都不会掷中, 通常出于容错的思量, 如果从存储层查不到数据则不写入缓存层。
缓存穿透将导致不存在的数据每次哀求都要到存储层去查询, 失去了缓存保护后端存储的意义。
造成缓存穿透的根本原因有两个:
第一, 自身业务代码或者数据出现标题。
第二, 一些恶意攻击、 爬虫等造成大量空掷中。
缓存穿透标题办理方案:
1、缓存空对象
2、布隆过滤器
对于恶意攻击,向服务器哀求大量不存在的数据造成的缓存穿透,还可以用布隆过滤器先做一次过滤,对于不存在的数据布隆过滤器一样平常都可以或许过滤掉,不让哀求再以后端发送。当布隆过滤器说某个值存在时,这个值大概不存在;当它说不存在时,那就肯定不存在。
布隆过滤器就是一个大型的位数组和几个不一样的无偏 hash 函数。所谓无偏就是可以或许把元素的 hash 值算得比力匀称。
向布隆过滤器中添加 key 时,会利用多个 hash 函数对 key 举行 hash 算得一个整数索引值然后对位数组长度举行取模运算得到一个位置,每个 hash 函数都会算得一个差别的位置。再把位数组的这几个位置都置为 1 就完成了 add 操纵。
向布隆过滤器扣问 key 是否存在时,跟 add 一样,也会把 hash 的几个位置都算出来,看看位数组中这几个位置是否都为 1,只要有一个位为 0,那么阐明布隆过滤器中这个key 不存在。如果都是 1,这并不能阐明这个 key 就肯定存在,只是极有大概存在,由于这些位被置为 1 大概是由于别的的 key 存在所致。如果这个位数组长度比力大,存在概率就会很大,如果这个位数组长度比力小,存在概率就会低落。
这种方法实用于数据掷中不高、 数据相对固定、 及时性低(通常是数据集较大) 的应用场景, 代码维护较为复杂, 但是缓存空间占用很少。
注意:布隆过滤器不能删除数据,如果要删除得重新初始化数据。
2.2 缓存失效(击穿)

由于大批量缓存在同一时间失效大概导致大量哀求同时穿透缓存直达数据库,大概会造成数据库刹时压力过大以致挂掉,对于这种情况我们在批量增长缓存时最好将这一批数据的缓存逾期时间设置为一个时间段内的差别时间。
2.3 缓存雪崩

缓存雪崩指的是缓存层支持不住或宕掉后, 流量会像奔逃的野牛一样, 打向后端存储层。
由于缓存层承载着大量哀求, 有效地保护了存储层, 但是如果缓存层由于某些原因不能提供服务(比如超大并发过来,缓存层支持不住,或者由于缓存操持欠好,类似大量哀求访问bigkey,导致缓存能支持的并发急剧降落), 于是大量哀求都会打到存储层, 存储层的调用量会暴增, 造成存储层也会级联宕机的情况。
防备和办理缓存雪崩标题, 可以从以下三个方面进举措手。
1) 包管缓存层服务高可用性,比如利用Redis Sentinel或Redis Cluster。
2) 依赖隔离组件为后端限流熔断并降级。比如利用Sentinel或Hystrix限流降级组件。
比如服务降级,我们可以针对差别的数据接纳差别的处置惩罚方式。当业务应用访问的黑白核心数据(比方电商商品属性,用户信息等)时,临时克制从缓存中查询这些数据,而是直接返回预界说的默认降级信息、空值或是错误提示信息;当业务应用访问的是核心数据(比方电商商品库存)时,仍然允许查询缓存,如果缓存缺失,也可以继续通过数据库读取。
3) 提前演练。 在项目上线前, 演练缓存层宕掉后, 应用以及后端的负载情况以及大概出现的标题, 在此根本上做一些预案设定。
热门缓存key重修优化
开发职员利用“缓存+逾期时间”的计谋既可以加快数据读写, 又包管数据的定期更新, 这种模式根本可以或许满足绝大部分需求。 但是有两个标题如果同时出现, 大概就会对应用造成致命的危害:

  • 当前key是一个热门key(比方一个热门的娱乐消息),并发量非常大。
  • 重修缓存不能在短时间完成, 大概是一个复杂盘算, 比方复杂的SQL、 多次IO、 多个依赖等。
在缓存失效的刹时, 有大量线程来重修缓存, 造成后端负载加大, 以致大概会让应用瓦解。
要办理这个标题紧张就是要克制大量线程同时重修缓存。
我们可以利用互斥锁来办理,此方法只允许一个线程重修缓存, 其他线程等待重修缓存的线程实行完, 重新从缓存获取数据即可。
2.4 缓存与数据库双写不同等

在大并发下,同时操纵数据库与缓存会存在数据不同等性标题
1、双写不同等情况

2、读写并发不同等

2.png 办理方案:
1、对于并发几率很小的数据(如个人维度的订单数据、用户数据等),这种险些不用思量这个标题,很少会发生缓存不同等,可以给缓存数据加上逾期时间,每隔一段时间触发读的自动更新即可。
2、就算并发很高,如果业务上能容忍短时间的缓存数据不同等(如商品名称,商品分类菜单等),缓存加上逾期时间依然可以办理大部分业务对于缓存的要求。
3、如果不能容忍缓存数据不同等,可以通过加分布式读写锁包管并发读写或写写的时间按次序排好队,读读的时间相当于无锁。
4、也可以用阿里开源的canal通过监听数据库的binlog日记及时的去修改缓存,但是引入了新的中心件,增长了体系的复杂度。
总结:
以上我们针对的都是读多写少的情况参加缓存提高性能,如果写多读多的情况又不能容忍缓存数据不同等,那就没须要加缓存了,可以直接操纵数据库。当然,如果数据库抗不住压力,还可以把缓存作为数据读写的主存储,异步将数据同步到数据库,数据库只是作为数据的备份。
放入缓存的数据应该是对及时性、同等性要求不是很高的数据。切记不要为了用缓存,同时又要包管绝对的同等性做大量的太过操持和控制,增长体系复杂性!
3.开发规范

3.1 键值操持

3.1.1 key名操持

(1)【发起】: 可读性和可管理性
以业务名(或数据库名)为前缀(防止key辩说),用冒号分隔,比如业务名:表名:id
traderder:1
(2)【发起】:轻巧性
包管语义的条件下,控制key的长度,当key较多时,内存占用也不容忽视,比方:
user:{uid}:friends:messages:{mid} 简化为 u:{uid}:fr:m:{mid}
(3)【欺压】:不要包罗特别字符
反例:包罗空格、换行、单双引号以及其他转义字符
3.1.2 value操持

(1)【欺压】:拒绝bigkey(防止网卡流量、慢查询)
在Redis中,一个字符串最大512MB,一个二级数据布局(比方hash、list、set、zset)可以存储约莫40亿个(2^32-1)个元素,但实际中如果下面两种情况,我就会以为它是bigkey。
字符串范例:它的big表如今单个value值很大,一样平常以为凌驾10KB就是bigkey。
非字符串范例:哈希、列表、聚集、有序聚集,它们的big表如今元素个数太多。
一样平常来说,string范例控制在10KB以内,hash、list、set、zset元素个数不要凌驾5000。
反例:一个包罗200万个元素的list。
非字符串的bigkey,不要利用del删除,利用hscan、sscan、zscan方式渐进式删除,同时要注意防止bigkey逾期时间自动删除标题(比方一个200万的zset设置1小时逾期,会触发del操纵,造成壅闭)
bigkey的危害:

  • 1.导致redis壅闭
  • 2.网络拥塞
    bigkey也就意味着每次获取要产生的网络流量较大,假设一个bigkey为1MB,客户端每秒访问量为1000,那么每秒产生1000MB的流量,对于普通的千兆网卡(按照字节算是128MB/s)的服务器来说简直是灭顶之灾,而且一样平常服务器会接纳单机多实例的方式来摆设,也就是说一个bigkey大概会对其他实例也造成影响,其效果不堪假想。

    • 逾期删除
      有个bigkey,它安分守己(只实行简朴的下令,比方hget、lpop、zscore等),但它设置了逾期时间,当它逾期后,会被删除,如果没有利用Redis 4.0的逾期异步删除(lazyfree-lazy-expire yes),就会存在壅闭Redis的大概性。
      bigkey的产生:
      一样平常来说,bigkey的产生都是由于步伐操持不当,或者对于数据规模预料不清楚造成的,来看几个例子:
      (1) 交际类:粉丝列表,如果某些明星或者大v不经心操持下,必是bigkey。
      (2) 统计类:比方按天存储某项功能或者网站的用户聚集,除非没几个人用,否则必是bigkey。
      (3) 缓存类:将数据从数据库load出来序列化放到Redis里,这个方式非常常用,但有两个地方必要注意,第一,是不是有须要把全部字段都缓存;第二,有没有干系关联的数据,有的同砚为了图方便把干系数据都存一个key下,产生bigkey。

怎样优化bigkey



    • big list: list1、list2、...listN
      big hash:可以讲数据分段存储,比如一个大的key,假设存了1百万的用户数据,可以拆分成200个key,每个key下面存放5000个用户数据


    • 如果bigkey不可克制,也要思考一下要不要每次把全部元素都取出来(比方偶然间仅仅必要hmget,而不是hgetall),删除也是一样,只管利用优雅的方式来处置惩罚。

(2)【保举】:选择适合的数据范例。
比方:实体范例(要合理控制和利用数据布局,但也要注意节省内存和性能之间的平衡)
反例:
set user:1:name tom
set user:1:age 19
set user:1:favor football
正例:
hmset user:1 name tom age 19 favor football
3.【保举】:控制key的生命周期,redis不是垃圾桶。
发起利用expire设置逾期时间(条件允允许以打散逾期时间,防止会集逾期)。
3.2 下令利用

1.【保举】 O(N)下令关注N的数目
比方hgetall、lrange、smembers、zrange、sinter等并非不能利用,但是必要明白N的值。有遍历的需求可以利用hscan、sscan、zscan取代。
2.【保举】:禁用下令
克制线上利用keys、flushall、flushdb等,通过redis的rename机制禁掉下令,或者利用scan的方式渐进式处置惩罚。
3.【保举】合理利用select
redis的多数据库较弱,利用数字举行区分,很多客户端支持较差,同时多业务用多数据库实际照旧单线程处置惩罚,会有干扰。
4.【保举】利用批量操纵提高服从
原生下令:比方mget、mset。
非原生下令:可以利用pipeline提高服从。
但要注意控制一次批量操纵的元素个数(比方500以内,实际也和元素字节数有关)。
注意两者差别:

  • 1)原生下令是原子操纵,pipeline黑白原子操纵。
  • 2)pipeline可以打包差别的下令,原生下令做不到
  • 3)pipeline必要客户端和服务端同时支持。
5.【发起】Redis事务功能较弱,不发起过多利用,可以用lua更换
3.3 客户端利用

1.【保举】
克制多个应用利用一个Redis实例
正例:不干系的业务拆分,公共数据做服务化。
2.【保举】
利用带有毗连池的数据库,可以有效控制毗连,同时提高服从。
优化发起:
1)maxTotal:最大毗连数,早期的版本叫maxActive
实际上这个是一个很难答复的标题,思量的因素比力多:
业务渴望Redis并发量
客户端实行下令时间
Redis资源:比方 nodes(比方应用个数) * maxTotal 是不能凌驾redis的最大毗连数maxclients。
资源开销:比方固然渴望控制空闲毗连(毗连池现在可立即利用的毗连),但是不渴望由于毗连池的频繁开释创建毗连造成不必靠开销。
以一个例子阐明,假设:
一次下令时间(borrow|return resource + Jedis实行下令(含网络) )的匀称耗时约为1ms,一个毗连的QPS约莫是1000
业务渴望的QPS是50000
那么理论上必要的资源池巨细是50000 / 1000 = 50个。但究竟上这是个理论值,还要思量到要比理论值预留一些资源,通常来讲maxTotal可以比理论值大一些。
但这个值不是越大越好,一方面毗连太多占用客户端和服务端资源,另一方面临于Redis这种高QPS的服务器,一个大下令的壅闭纵然设置再大资源池仍然会无济于事。
2)maxIdle和minIdle
maxIdle实际上才是业务必要的最大毗连数,maxTotal是为了给出余量,以是maxIdle不要设置过小,否则会有new Jedis(新毗连)开销。
毗连池的最佳性能是maxTotal = maxIdle,这样就克制毗连池伸缩带来的性醒目扰。但是如果并发量不大或者maxTotal设置过高,会导致不须要的毗连资源浪费。一样平常保举maxIdle可以设置为按上面的业务渴望QPS盘算出来的理论毗连数,maxTotal可以再放大一倍。
minIdle(最小空闲毗连数),与其说是最小空闲毗连数,不如说是"至少必要保持的空闲毗连数",在利用毗连的过程中,如果毗连数凌驾了minIdle,那么继续创建毗连,如果凌驾了maxIdle,当凌驾的毗连实行完业务后会徐徐被移出毗连池开释掉。
如果体系启动完立即就会有很多的哀求过来,那么可以给redis毗连池做预热,比如快速的创建一些redis毗连,实行简朴下令,类似ping(),快速的将毗连池里的空闲毗连提升到minIdle的数目。
总之,要根据实际体系的QPS和调用redis客户端的规模团体评估每个节点所利用的毗连池巨细。
3.【发起】
高并发下发起客户端添加熔断功能(比方sentinel、hystrix)
4.【保举】
设置合理的暗码,如有须要可以利用SSL加密访问
5.【发起】
Redis对于逾期键有三种扫除计谋:
被动删除:当读/写一个已颠末期的key时,会触发惰性删除计谋,直接删撤除这个逾期key
自动删除:由于惰性删除计谋无法包管冷数据被及时删掉,以是Redis会定期(默认每100ms)自动镌汰一批已逾期的key,这里的一批只是部分逾期key,以是大概会出现部分key已颠末期但还没有被清算掉的情况,导致内存并没有被开释
当前已用内存凌驾maxmemory限定时,触发自动清算计谋
自动清算计谋在Redis 4.0 之前一共实现了 6 种内存镌汰计谋,在 4.0 之后,又增长了 2 种计谋,统共8种:
a) 针对设置了逾期时间的key做处置惩罚:
volatile-ttl:在筛选时,会针对设置了逾期时间的键值对,根据逾期时间的先后举行删除,越早逾期的越先被删除。
volatile-random:就像它的名称一样,在设置了逾期时间的键值对中,举行随机删除。
volatile-lru:会利用 LRU 算法筛选设置了逾期时间的键值对删除。
volatile-lfu:会利用 LFU 算法筛选设置了逾期时间的键值对删除。
b) 针对全部的key做处置惩罚:
allkeys-random:从全部键值对中随机选择并删除数据。
allkeys-lru:利用 LRU 算法在全部数据中举行筛选删除。
allkeys-lfu:利用 LFU 算法在全部数据中举行筛选删除。
c) 不处置惩罚:
noeviction:不会剔除任何数据,拒绝全部写入操纵并返回客户端错误信息"(error) OOM command not allowed when used memory",此时Redis只相应读操纵。
LRU 算法(Least Recently Used,近来最少利用)
镌汰好久没被访问过的数据,以近来一次访问时间作为参考。
LFU 算法(Least Frequently Used,最不常常利用)
镌汰近来一段时间被访问次数最少的数据,以次数作为参考。
当存在热门数据时,LRU的服从很好,但偶发性的、周期性的批量操纵会导致LRU掷中率急剧降落,缓存污染情况比力严峻。这时利用LFU大概更好点。
根据自身业务范例,设置好maxmemory-policy(默认是noeviction),保举利用volatile-lru。如果不设置最大内存,当 Redis 内存超出物理内存限定时,内存的数据会开始和磁盘产生频繁的互换 (swap),会让 Redis 的性能急剧降落。
当Redis运行在主从模式时,只有主结点才会实行逾期删除计谋,然后把删除操纵”del key”同步到从结点删除数据。
3.4 体系内核参数优化

vm.swapiness
swap对于操纵体系来说比力紧张,当物理内存不敷时,可以将一部分内存页举行swap到硬盘上,以解燃眉之急。但天下上没有免费午餐,swap空间由硬盘提供,对于必要高并发、高吞吐的应用来说,磁盘IO通常会成为体系瓶颈。在Linux中,并不是要比及全部物理内存都利用完才会利用到swap,体系参数swppiness会决定操纵体系利用swap的倾向程度。swappiness的取值范围是0~100,swappiness的值越大,阐明操纵体系大概利用swap的概率越高,swappiness值越低,体现操纵体系更加倾向于利用物理内存。swappiness的取值越大,阐明操纵体系大概利用swap的概率越高,越低则越倾向于利用物理内存。
如果linux内核版本<3.5,那么swapiness设置为0,这样体系甘心swap也不会oom killer(杀掉历程)
如果linux内核版本>=3.5,那么swapiness设置为1,这样体系甘心swap也不会oom killer
一样平常必要包管redis不会被kill掉:
cat /proc/version  #查察linux内核版本
echo 1 > /proc/sys/vm/swappiness
echo vm.swapiness=1 >> /etc/sysctl.conf
PS:OOM killer 机制是指Linux操纵体系发现可用内存不敷时,欺压杀死一些用户历程(非内核历程),来包管体系有充足的可用内存举行分配。
vm.overcommit_memory(默认0)
0:体现内核将查抄是否有充足的可用物理内存(实际不愿定用满)供应用历程利用;如果有充足的可用物理内存,内存申请允许;否则,内存申请失败,并把错误返回给应用历程 
1:体现内核允许分配全部的物理内存,而不管当前的内存状态怎样
如果是0的话,大概导致类似fork等操纵实行失败,申请不到充足的内存空间
Redis发起把这个值设置为1,就是为了让fork操纵可以或许在低内存下也实行乐成。
cat /proc/sys/vm/overcommit_memory
echo "vm.overcommit_memory=1" >> /etc/sysctl.conf
sysctl vm.overcommit_memory=1
合理设置文件句柄数
操纵体系历程试图打开一个文件(或者叫句柄),但是如今历程打开的句柄数已经到达了上限,继续打开会报错:“Too many open files”
ulimit -a  #查察体系文件句柄数,看open files那项
ulimit -n 65535  #设置体系文件句柄数
慢查询日记:slowlog
Redis慢日记下令阐明:
config get slow* #查询有关慢日记的设置信息
config set slowlog-log-slower-than 20000  #设置慢日记使时间阈值,单位微秒,此处为20毫秒,即凌驾20毫秒的操纵都会记载下来,生产情况发起设置1000,也就是1ms,这样理论上redis并发至少到达1000,如果要求单机并发到达1万以上,这个值可以设置为100
config set slowlog-max-len 1024  #设置慢日记记载生存数目,如果生存数目已满,会删除最早的记载,最新的记载追加进来。记载慢查询日记时Redis会对长命令做截断操纵,并不会占用大量内存,发起设置稍大些,防止丢失日记
config rewrite #将服务器当前所利用的设置生存到redis.conf
slowlog len #获取慢查询日记列表的当前长度
slowlog get 5 #获取最新的5条慢查询日记。慢查询日记由四个属性构成:标识ID,发生时间戳,下令耗时,实行下令和参数
slowlog reset #重置慢查询日记
4.Redis为什么这么快?

Redis 基于 Reactor 模式开发了本身的网络变乱处置惩罚器 - 文件变乱处置惩罚器(file event handler,后文简称为 FEH),而该处置惩罚器又是单线程的,以是redis操持为单线程模子。
接纳I/O多路复用同时监听多个socket,根据socket当前实行的变乱来为 socket 选择对应的变乱处置惩罚器。
当被监听的socket准备好实行accept、read、write、close等操纵时,和操纵对应的文件变乱就会产生,这时FEH就会调用socket之前关联好的变乱处置惩罚器来处置惩罚对应变乱。
以是固然FEH是单线程运行,但通过I/O多路复用监听多个socket,不但实现高性能的网络通讯模子,又能和 Redis 服务器中别的同样单线程运行的模块交互,包管了Redis内部单线程模子的轻巧操持。
4.1 Redis6中的多线程

1. Redis6.0之前的版本真的是单线程吗?
Redis在处置惩罚客户端的哀求时,包罗获取 (socket 读)、分析、实行、内容返回 (socket 写) 等都由一个次序串行的主线程处置惩罚,这就是所谓的“单线程”。但如果严格来讲从Redis4.0之后并不是单线程,除了主线程外,它也有背景线程在处置惩罚一些较为迟钝的操纵,比方清算脏数据、无用毗连的开释、大 key 的删除等等。
2. Redis6.0之前为什么不绝倒霉用多线程?
官方曾做过类似标题标复兴:利用Redis时,险些不存在CPU成为瓶颈的情况, Redis紧张受限于内存和网络。比方在一个普通的Linux体系上,Redis通过利用pipelining每秒可以处置惩罚100万个哀求,以是如果应用步伐紧张利用O(N)或O(log(N))的下令,它险些不会占用太多CPU。
利用了单线程后,可维护性高。多线程模子固然在某些方面体现优秀,但是它却引入了步伐实行次序的不确定性,带来了并发读写的一系列标题,增长了体系复杂度、同时大概存在线程切换、以致加锁解锁、死锁造成的性能消耗。Redis通过AE变乱模子以及IO多路复用等技能,处置惩罚性能非常高,因此没有须要利用多线程。单线程机制使得 Redis 内部实现的复杂度大大低落,Hash 的惰性 Rehash、Lpush 等等 “线程不安全” 的下令都可以无锁举行。
3. Redis6.0为什么要引入多线程呢?
Redis将全部数据放在内存中,内存的相应时长约莫为100纳秒,对于小数据包,Redis服务器可以处置惩罚80,000到100,000 QPS,这也是Redis处置惩罚的极限了,对于80%的公司来说,单线程的Redis已经充足利用了。
但随着越来越复杂的业务场景,有些公司动不动就上亿的买卖业务量,因此必要更大的QPS。常见的办理方案是在分布式架构中对数据举行分区并接纳多个服务器,但该方案有非常大的缺点,比方要管理的Redis服务器太多,维护代价大;某些实用于单个Redis服务器的下令不实用于数据分区;数据分区无法办理热门读/写标题;数据偏斜,重新分配和放大/缩小变得更加复杂等等。
从Redis自身角度来说,由于读写网络的read/write体系调用占用了Redis实行期间大部分CPU时间,瓶颈紧张在于网络的 IO 斲丧, 优化紧张有两个方向:
• 提高网络 IO 性能,典范的实现比如利用 DPDK 来更换内核网络栈的方式
• 利用多线程充实利用多核,典范的实现比如 Memcached。
协议栈优化的这种方式跟 Redis 关系不大,支持多线程是一种最有效最便捷的操纵方式。以是总结起来,redis支持多线程紧张就是两个原因:
• 可以充实利用服务器 CPU 资源,现在主线程只能利用一个核
• 多线程使命可以分摊 Redis 同步 IO 读写负荷
4.Redis6.0默认是否开启了多线程?
Redis6.0的多线程默认是禁用的,只利用主线程。如需开启必要修改redis.conf设置文件:io-threads-do-reads yes
开启多线程后,还必要设置线程数,否则是不见效的。
关于线程数的设置,官方有一个发起:4核的呆板发起设置为2或3个线程,8核的发起设置为6个线程,线程数肯定要小于呆板核数。还必要注意的是,线程数并不是越大越好,官方以为凌驾了8个根本就没什么意义了。
5.Redis6.0接纳多线程后,性能的提升效果怎样?
Redis 作者 antirez 在 RedisConf 2019分享时曾提到:Redis 6 引入的多线程 IO 特性对性能提升至少是一倍以上。国内也有大牛曾利用unstable版本在阿里云esc举行过测试,GET/SET 下令在4线程 IO时性能相比单线程是险些是翻倍了。如果开启多线程,至少要4核的呆板,且Redis实例已经占用相当大的CPU耗时的时间才发起接纳,否则利用多线程没故意义。
6.Redis6.0多线程的实现机制?
流程简述如下:
1)主线程负责吸收创建毗连哀求,获取 socket 放入全局等待读处置惩罚队列
2)主线程处置惩罚完读变乱之后,通过 RR(Round Robin) 将这些毗连分配给这些 IO 线程
3)主线程壅闭等待 IO 线程读取 socket 完毕
4)主线程通过单线程的方式实行哀求下令,哀求数据读取并分析完成,但并不实行回写 socket
5)主线程壅闭等待 IO 线程将数据回写 socket 完毕
6)清除绑定,清空等待队列
该操持有如下特点:
1)IO 线程要么同时在读 socket,要么同时在写,不会同时读或写
2)IO 线程只负责读写 socket 分析下令,不负责下令处置惩罚
7.开启多线程后,是否会存在线程并发安全标题?
从上面的实现机制可以看出,Redis的多线程部分只是用来处置惩罚网络数据的读写和协议分析,实行下令仍然是单线程次序实行。以是我们不必要去思量控制 key、lua、事务,LPUSH/LPOP 等等的并发及线程安全标题。
8.Redis6.0的多线程和Memcached多线程模子举行对比
Memcached 服务器接纳 master-woker 模式举行工作,服务端接纳 socket 与客户端通讯。主线程、工作线程 接纳 pipe管道举行通讯。主线程接纳 libevent 监听 listen、accept 的读变乱,变乱相应后将毗连信息的数据布局封装起来,根据算法选择符合的工作线程,将毗连使命携带毗连信息分发出去,相应的线程利用毗连形貌符创建与客户端的socket毗连 并举行后续的存取数据操纵。
雷同点:都接纳了 master线程-worker 线程的模子
差别点:Memcached 实行主逻辑也是在 worker 线程里,模子更加简朴,实现了真正的线程隔离,符合我们对线程隔离的通例明白。而 Redis 把处置惩罚逻辑交还给 master 线程,固然肯定程度上增长了模子复杂度,但也办理了线程并发安全等标题
参考


  • 图灵学院课程,https://vip.tulingxueyuan.cn/
您需要登录后才可以回帖 登录 | 立即注册

Powered by CangBaoKu v1.0 小黑屋藏宝库It社区( 冀ICP备14008649号 )

GMT+8, 2024-11-22 00:34, Processed in 0.177021 second(s), 35 queries.© 2003-2025 cbk Team.

快速回复 返回顶部 返回列表