Redis基础知识总结(面试必备)

栏目: IT技术 · 发布时间: 4年前

内容简介:1. Redis对集群的支持情况

Redis基础知识总结(面试必备)

Redis组件因其开源免费、出色的读写性能,备受广大互联网公司的热爱,许多公司用 Redis 作为缓存来抗住C端的请求,若没有Redis,许多公司的业务将不堪一击。Redis可以作为单机缓存(业务进程重启后,内容不会丢失,代替共享内存的使用),或者通过集群的方式作为服务后端的缓存、提供可无限扩展的读写能力(只要服务器资源足够),也有公司将Redis集群作为高可靠的KV存储(开启持久化选项)。在面试过程中,Redis的相关知识也是问的最多的,可以说是必问必会的知识点。以前在使用Redis的过程中做的学习笔记,现整理做一下分享。

1. Redis对集群的支持情况

  • redis 3.0之前版本不支持集群,若要实现集群,则需要借助中间件来实现存值和取值的对应节点。

  • redis3.0之后的版本(含3.0),搭建集群,集群中机器两两连接,每台机器都可以做中转,事先给每台机器分配槽点(总共16384个),对于存值和取值,根据key来映射对应槽点和机器节点,若key不属于当前节点则跳转到对应其他节点,相当于每台机器节点都是一个全局路由。

2. Redis集群投票机制

Redis集群通过Gossip通信,当某个节点发现另外一个节点失联的时候,对外广播PFail信息,当某个节点收到集群绝大多数节点都判定那个节点为PFail的时候,强制将那个节点设置为Fail、并广播出去让其他节点接受、并立即对该节点进行主从切换。cluster-node-timeout ,当某个节点持续timeout时间失联,才认为该节点故障,需要主从切换,从而避免频繁的主从切换(数据的重新复制)。

3. Redis的淘汰策略

  • volatile_lru:从设置过期的key中,挑选最近最少使用的key。

  • volatile_ttl:从设置过期的key中,挑选即将过期的key。

  • volatile_random:从设置过期的key中,随机挑选一个key。

  • allkeys_lru:从数据集中,挑选最近最少使用的key。

  • allkeys_random:从数据集中,随机挑选一个key。

  • no_enviction:禁止淘汰数据(还是会根据引用计数进行内存释放),当内存使用超过设置的阈值,若再有内存申请操作则会失败。

4. Redis解决并发问题的思路

  • 采用单进程单线程模式,没有锁的概念

  • 通过队列将并发访问串行化

  • 通过setnx命令,若不存在则设置对应的key

  • 多路复用,保证高并发,定时任务,小顶堆,将最近最快要执行的任务放在堆顶,距离要执行的时间为多路复用的timeout,因为redis知道这段时间是没有任务要执行的,可以安心sleep,QPS可以达到10w/s。

5. Redis内存回收

  • 对于不再引用或者过期的key,采用惰性和定时任务检测的方式,用户有请求过来、同时判断此key是否过期,若过期则删除并返回空,定时任务随机选取一些key(可配置),判断是否过期,对于Redis单进程单线程的架构来说,这样可以节省CPU和内存。定时任务每个数据库随机抽检20个key,发现过期则删除,若超过检查数目的 25% 的key过期,则循环执行,直到不足25%或者超过执行时间。

  • 当设置了最大使用内存后、并且内存使用已经超过设置阈值,则采用上述淘汰策略进行内存回收。

6. Redis的数据类型

  • string:动态的字符串,可以修改,当字符串长度小于1MB的时候,扩充都是加倍现有空间,超过1MB的话,扩充一次最多增加1MB,最大512MB。

  • list:类似于 java 的linklist,插入和删除很快,O(1),但是查找很慢,O(n), 当弹出list的最后一个元素后,该list自动被删除,内存被回收。ziplist和双向链表结合来实现quicklist,ziplist里面是一块连续的内存,是经过压缩的,因为对于int等基础类型,额外的两个指针太耗费空间,存储方式类似于LevelDb里面的Block,最后通过双向链表将ziplist连起来,这样既满足快速插入、删除的特性,空间浪费也不多。

  • hash:相当于java的HashMap,无序字典,内部实现是用数组+链表,第一维的数组出现碰撞,则存到链表里面去。Rehash的时候,为了不阻塞服务,采用的是渐进式的Rehash,保留2个Hash,逐渐在指令执行或者定时任务中,将数据从老的Hash迁移到新的Hash。

  • set:相当于java的HashSet,无序的键值对,不过其值都是NULL,键不可以重复。数据量较少且是整数的时候用有序数组,较大的时候采用散列表。

  • zset:最具特色的数据结构,也是面试官问的最多的知识点。通过散列表+跳表实现,一方面是一个set,保证键的唯一性,另一方面,可以给每个value赋一个score,可以根据score排序、区段查找。应用场景:value为粉丝ID,score是关注时间,可以按照关注时间顺序给出粉丝ID。value是学生ID,score是其分数,可以按照分数排序。

7. Redis的过期设置

Redis所有的数据结构都可以设置过期时间,比如hash,需要注意,只能对hash整体设置过期时间,无法对其中的单个field设置过期时间。若一个对象已经设置了过期时间,但是待会你又修改了他,那么他的过期时间就会消失,也就是需要重新设置过期时间。

8. Redis分布式锁的一些讨论

redis是单进程单线程,本身没有锁的概念,主要是应用分布在多台机器,客户端不同顺序访问引起的互斥问题,例如,A读取字段field1,然后修改设置,B也同时读取字段field1(在A修改生效前),然后也修改设置(在A修改生效后),这样B的修改就将A的修改覆盖掉了,下面是递进的几种改进思路。

  • setnx key value; del key,若程序异常,del没有执行的话,则其他程序永远得不到锁了。

  • setnx key value; expire key 超时时间(单位秒);del key,加一个超时命令,似乎能解决问题,但是也存在同样的问题,即在执行expire之前程序就可能core dump了。

  • set key value ex 超时时间(单位秒) nx; del key,这个基本没有问题了,但是业务执行时间不能超过超时时间,否则可能也会有问题。例如,程序A获得了锁,并执行,过了设置的超时时间后,锁自动释放,程序B获得锁,这个时候程序A执行完毕,将锁删除了。这样程序C就能获得锁,B和C可能会产生冲突。

  • 如果产生锁的时候随机生成一个数,删除的时候先check一下随机数是否相同,只有相同了才能删除锁(即是当时生产锁的责任人)。Redis本身没有这个机制(检测和删除不是原子的),不过可以通过 lua 脚本来实现。

  • 若是Redis集群,主从切换期间,程序A先在主机申请获得了一把锁,这时候发生主从切换,从机还没有感知到那把锁(即还没有将对应key同步过来),然后程序B又在新的主机(原来的从机)申请获得了一把同样的锁,这样业务就会出现问题。出现这个情况,本质上是因为Redis集群是异步复制(先给客户端回包,再主->从replication),不像Paxos等强一致性保证。

Redis提供的分布式锁RedLock:

  • 分布式锁通常采用租约的形式,即设定的一个超时时间,避免某个获得锁的客户端挂死导致锁一直得不到释放。

  • 租约锁通常是不安全的,例如,程序A获得锁,尝试修改某些信息,在这期间可能因为网络、java等的GC机制导致程序暂停,很可能就过了租约期了即使在前面加多次check租约是否到期的逻辑、也可能没有用。这个时候程序B获得锁,并发的修改的某些受控信息,这就会引起混乱。

  • 给资源加上fencing token,递增的数字token,这个时候,配合的,存储服务需要check 一下token的值,只能递增的修改,不能回头,比如token 34修改过了,token 33就不能修改了。

  • RedLock没有提供fenciing token 机制, 随机数不能解决这个问题,单机提供一个计数器也不行,若要多机的话,需要一个consensus算法生成fencing token。

  • 通常,分布式一致性算法,是不会去依赖时间的,性能可能很差,但是绝不会出错,比如paxos。

  • RedLock 做了很多时间假设,假设网络延迟等时间、程序因为GC等暂停的时间比锁的租约时间小,redis某节点拥有的key的时间也小于锁租约时间。

  • 作者的一些建议:1)如果用锁是为了效率(避免做多次做很重的活),可以直接用单例redis(conditional set-if-not-exists to obtain a lock, atomic delete-if-value-matches to release a lock),不用太多担心;2)如果用锁是为了正确,请不要用RedLock, 不靠谱,应该用zk等。

9. Redis中使用lua脚本的好处及其限制

  • lua脚本中的命令一起执行,是原子的,不用担心被打断,有效解决多个客户端的读+修改+回写引起的冲突问题。

  • 可以定制自己的命令,并常驻内存。

  • 有效降少客户单、服务器之间的交互。

  • lua脚本不要执行太长时间,默认是不超过5秒。

  • redis+lua适合单机,不适合集群,主要是没有实现调度,一个脚本中的所有key要在同一个槽点执行。

  • 为了安全考虑,屏蔽了很多命令。

  • 阿里云redis集群支持lua,不过对脚本做了如下限制:1)所有key应该由keys数组传递,2)所有key必须在一个slot上,3)调用必须带key。

10. hyperloglog

场景举例,往集合里面添加100w条记录,计算其中不重复的数据个数,若用set去重,会占用很多空间,使用hyperloglog只用12KB,但是会损失一些精准度,误差在0.81%左右。

11. Redis中的布隆过滤组件

布隆过滤器可以作为一个插件加载到redis server:

redis-server --loadmodule /path/to/redisbloom.so

一个大型的位数组和若干无偏Hash函数,多个Hash函数保证元素的hash值算的均匀一点。

  • add操作:经过若干Hash函数,计算出若干hash值,和位数组大小取模后得到数组的索引,然后将对应位置设置为1.

  • exists操作:和add一样,先经过若干Hash函数,计算出若干hash值,和位数组大小取模后得到一些数组的索引,判断这些索引对应的位置的值,只要有一个为0,则说明不存在。如果都为1的话,则说明存在。”存在“的判断可能会有误判,因为可能是其他内容设置的值,”不存在“的判断没有误判。

12. Redis限流模块

redis-cell,rust语言编写的基于令牌桶算法的限流模块,举例:

cl.throttle "svr:itf" 15 30 60 1  

其中,

"svr:itf":限流对象

15:capacity, 桶的容量

30:操作次数

60:60s,和上面一个值表示,在60s内最多有30次操作

1:可选参数,默认值是1,代表一个操作。

针对某个限流微服务接口,预先设置好访问频率,每次请求过来,执行类似的如上命令,若返回0代表可以调用,若返回1则表示不可以调用。若一段时间后,接口频率增大了,可以重新使用此命令设置一次(调整桶的容量和频次)。

13. GeoHash算法

地理位置距离 排序 算法GeoHash算法。

经度:(-180, 180],以本初子午线(英国格林尼治天文台)东正西负。

纬度:(-90, 90],以赤道为界,北正南负。

将地球想象成一个平面,用正方形方格分隔它,最终将二维坐标映射成一个整数,整数越长,精度越精确。通过zset结构存储,value是地址,score是上面映射的52位整数,通过zset的score排序,就可以得到附近的人的坐标(整数还原成二维坐标)

  • 添加经纬度信息:geoadd key longitude latitude member [longitude latitude member  ...]

  • 计算两者距离:geodist key member1 member2 [unit]

  • 获取集合中元素的地理信息:geopos key member [member ...]

  • 获取某个地点的geohash编码:geohash key member[member ...]

  • 查找指定元素附近的其他元素:1)georadiusbymember key member radius m|km|ft|mi [WITHCOORD][WITHDIST][COUNT count][ASC|DESC][STORE key][STOREDIST key], 2) georadius key longitude latitude radius m|km|ft|mi [WITHCOORD][WITHDIST][COUNT count][ASC|DESC][STORE key][STOREDIST key]

geoXXX 操作的是一个zset结构,假设key为company,则可以通过del company 来删除整个集合,geo没有提供删除单个元素的接口,不过可以通过zrem key member来删除某个值。需要注意geo结构的大小(zset),若用redis集群,结果过大会给集群迁移造成停顿,可以按照国家/城市/区做划分,前面再加一个地点路由,减少key的数量。

14. Redis的scan

通过游标、分步骤执行,不阻塞线程,不影响其他指令的执行,通过limit限制每次吐出的数据,可多可少,返回结果可能是重复的,需要客户端去重,服务器不保存游标状态,唯一的游标状态是返回的游标整数,单次返回空不代表遍历结束,遍历是否结束要看返回的游标整数是否为0。在遍历过程中,修改后的值能否遍历到,这是不确定的。

scan x match y count limit , x~游标,从0开始,本次scan会返回一个游标,下次scan用那个返回的游标,直到为0才算结束,y~key的正则匹配,limit~每次返回的limit(实际是遍历的槽位数目),只是一个参考。

key的的存储结构:一维数组+二维链表,一维数组也就是槽位,limit指的是这个,之所以返回的数量有多有少,是因为并不是所有槽位上面都挂有链表。

扩容是按照一维数组来的,为了避免扩容、缩容的过程中重复遍历元素,采用高位加法(从左到右开始加并进位)。

zscan对zset扫描,hscan对hash扫描,sscan对set扫描。

15. 大key问题

大key对redis是一个挑战,包括redis集群(集群会迁移节点),内存申请、扩容、删除等场景造成卡顿。

scan->type指令得到各个数据类似->用各个数据结构的size/len计算大小,对每种类型,保留topN,通过这种方式找到大key。

官方脚本:redis-cli -h 127.0.0.1 -p 6379 -bigkeys -i 0.1 ,  -i 0.1 代表每执行100条指令休息0.1秒。

16. Redis通信协议

直观的文本协议,实现简单,解析性能好,单元结束后统一加上\r\n。

  • 单行字符串,以+符号开始 ,+hello world\r\n

  • 多行字符串,以$符号开始,后跟字符串长度 $11\r\nhello world\r\n

  • 整数值,以:符号开始,后跟整数的字符串形式 :1024\r\n

  • 错误消息,以-符号开始, -WRONGTYPE Operation against a key holding the wrong kind of value

  • 数组,以*开始,后跟数组的长度,*3\r\n:1\r\n:2\r\n:3\r\n

  • NULL用多行字符串表示,不过长度要写成-1, $-1\r\n

  • 空行用多行字符串表示,长度填0,$0\r\n

17. Redis的持久化

  • 快照(rdb文件),全量备份,内存数据的二进制序列化,非常紧凑,通过COW(Copy on Write)机制,fork一个子进程专门用于写磁盘,父进程继续接受请求进行处理,一开始父子进程共享数据段,当父进程有写操作的时候,会在写之前单独拷贝一份内容给子进程(以页为单位),这样虽然会导致内存增大,但是可以保证数据在某一时刻的一致性,即“快照”,通过save/bgsave 产生快照。

  • AOF日志,连续的增量备份,内存修改记录的指令文本,需要定期重写,只存储修改指令,指令参数校验没有问题后,写AOF日志、然后执行指令,长时间的运行会导致AOF日志过大,系统重启后的回放也很耗时。通过bgrewriteaof命令开启子进程进行日志瘦身,将可以合并的写指令进行合并。fsync,控制刷盘时间,通常1s一次,在性能和安全之间做权衡。

  • 混合持久化,快照(rdb文件)和AOF同时使用,此时AOF不是增量备份文件,而是快照落盘开始到落盘结束这段时间的增量内容。

通常主节点不持久化,由从节点进行持久化,适当增加从节点的个数,保证数据安全。

18. pipeline与事务

为提高执行效率,通过pipeline一次执行多个命令,且整体当做一个事务,满足隔离性但是不满足原子性,比如3个命令,第二个失败了,第三个会继续执行。pipeline不是服务端的特性,是客户端的特性,客户端通过改变读写顺序带来了性能的巨大提升(前提是pipeline中的多条命令没有前后数据依赖,通常也不会有的)。

Redis基础知识总结(面试必备)

19. Redis内存占用

32bit位编译,内存上限是4GB,可以使指针等内存减少一半,可以结合多实例来突破4GB的限制。

小对象压缩存储ziplist见下图,若是hash,key和value作为两个entry相邻在一起,若是zset,value和score作为两个entry相邻在一起。

Redis基础知识总结(面试必备)

intset:紧凑型set,用于整数且数量较小的set集合,若有字符串,则立即升级为hashtable结构。不同的数据结构,当数量或者元素大小较小的时候,用紧凑型存储,当超过规定后,开始用标准存储,一般元素个数是512,元素大小是64字节。

下面是各个控制参数的意义:

  • hash-max-zipmap-entries 512 : hash的元素个数超过512就必须使用标准结构存储。

  • hash-max-zipmap-value 64:hash的任意元素的key/value的长度超过64就必须使用标准结构存储。

  • list-max-ziplist-entries 512 : list的元素个数超过512就必须使用标准结构存储。

  • list-max-ziplist-value 64:list的任意元素的长度超过64就必须使用标准结构存储。

  • zset-max-ziplist-entries 128 : zset的元素个数超过128就必须使用标准结构存储。

  • zset-max-ziplist-value 64:zset的任意元素的长度超过64就必须使用标准结构存储。

  • set-max-intset-entries 512 : set的元素个数超过512就必须使用标准结构存储。

20. Redis内存分配与回收

Redis没有管内存分配,直接采用第三方内存分配算法,libc、jemalloc,tcmalloc。

删除key后并不会让内存立即释放,因为操作系统回收是以页为单位的,只要这一页还有1个key在使用,就无法回收。Redis会重用已经删除的key的内存的。

21. Redis主从同步

CAP原理:C-一致性,A-可用性,P-分区容忍性, 当网络发生故障(网络分区),分布的节点无法互相访问:

  • 若保证一致性,则无法对外提供修改(若能修改,则必然不一致了)。

  • 若保证可用性(可修改),则无法保证一致性。

Redis是异步同步,不满足一致性,满足可用性,但是保证最终一致性,支持主从同步和从从同步,同步方式如下,主从复制是Redis分布式的基础,Redis的高可用离开主从复制则无从谈起,集群模式都依赖主从复制。

  • 增量同步,同步的是修改状态的指令流,指令流有一个固定的buffer,循环使用,绕一圈则覆盖,主机将指令流同步给从机,从机执行指令流,并告知主机的指令偏移。

  • 快照同步,先在主库上面执行bgsave将当前内存的数据全部快照到磁盘,拷贝到从机,执行全量加载,加载完毕后通知主机进行增量同步,若快照过大,在快照恢复期间,主机的指令流buffer满了,从机即使加载了快照仍然无法从主机同步增量,这就需要重新弄一变快照,极端情况可能会死循环,所以要合理设置buffer大小。

  • 无盘复制:主机通过套接字将快照内容发给从机,生成快照是一个遍历的过程,一边遍历,一边将内容序列化后发给从机,从机接受后先保存到本地文件,后面再一次性load。

Redis的复制默认是异步,wait指令可以将异步变成同步,确保系统强一致,不过这会一定程度上影响主机的执行效率。 wait param1 param2, param1~从机的数量,param2~时间,最多等待param2毫秒,为0则表示无限等待直到N个从库同步完成。

22. Redis Sentinel 模式

类似一个zk集群,多机,专门负责Redis的主从切换,客户端先请求Sentinel,Sentinel负责分配主机地址给客户端,后续客户端直接和给定的主机地址通信。若Redis主机挂掉,Sentinel会识别出来,并从多个从机中选择一个作为主机,原先挂掉的主机恢复后成为从机,和新的主机进行同步,客户端原先的旧的链接会断掉、重新连接、或者抛出readonly异常(旧的主机恢复后变成从机,只读,不能修改)。 min-slaves-to-write 1 主节点至少有一个从节点在正常同步,否则停止对外提供写操作。min-slaves-max-lag 10 如果10s内没有收到从节点的反馈,则说明从节点同步异常。

Sentinel切换主从,客户端如何感知:

  1. redis-py 建立链接的时候,会和库内主地址比较,若不一样,则会断掉所有链接、重新创建链接。

  2. 若是原先主库异常恢复后降为从机,原先链接的写操作会抛ReadOnly错误,检测到此错误后会断掉旧链接、创建新链接。

  3. 接续第二点,若只有读操作,链接没有切换,这样也没有危险,让他继续好了(若没有来得及同步,则可能会有一点不一致)。

23. Codis,分而治之

go语言开发,对外提供的协议和Redis一样,外界无感知,代理中间件,Codis后面挂多个Redis实例,形成一个Redis集群,可以在线扩容。

通过zk、etcd持久化槽位信息,Codis proxy 通过监听槽位变化并同步槽位信息,从而实现多个Codis proxy共享相同的槽位信息。

Codis内存维护槽位和Redis实例的关系,例如1024个槽位,根据key计算相应的槽位(crc32 hash然后取1024余数),然后根据槽位得到Redis实例。

hash = crc32(command.key)

slot_index = hash % 1024

redis = slot[slot_index].redis

redis.do(command)

增加Redis实例,原先槽位和Redis的实例的对应关系会发生变化,若是迁移过程中发生key访问,则强制将此key迁移到新的redis实例,同时将请求转到新的redis实例,待对应槽位下的key都迁移完毕后,刷新槽位和redis实例的对应关系。

slot_index = crc32(command.key) % 1024

if slot_index in migrating_slots:

do_migrate_key(command.key)

redis = slots[slot_index].new_redis

else:

redis = slots[slot_index].redis

redis.do(command)

单个key不宜过大,比如hash结构,会用hgetall拉取所有内容,然后用hmset放置到另外一个节点,如果hash内的field~value过多,则会给迁移带来卡顿,官方建议不超过1MB。

24. Redis Cluster(官方集群方式)

数据划分为16384个槽位,key经过crc32后取16384余数,决定key落在哪个槽位,也可以显示指定key为某个槽位,每个node节点维护集群槽位和所有node节点的映射关系。当请求落在某个不包含指定槽位的node节点,此节点会跳转(含目标node信息)信息给客户端,客户端重新和目标node通信。为防止多次重定向,客户端一般都会缓存槽位和node节点的映射关系,并根据服务端node节点返回的重定向信息更新本地缓存。

Redis集群可以重新调整槽位在不同node节点的分布,比如某个node节点 有1000个槽位节点,内存快要满了,这就需要将部分槽位迁移到内存低的node节点。

槽位迁移的中间状态:migrating->importing,获取槽位的所有key,再挨个以key为单位进行迁移,从源节点到目标节点,源节点dump key的内容,发送给目标节点,目标节点restore,成功后返回源节点ok,源节点将对应key删除,目标节点执行restore和源节点删除key之间,源节点的主线程会处于阻塞状态,直到key被删除,key小比较好,如果key较大,迁移会产生影响,造成集群抖动。

迁移过程中的客户端访问流程:

  1. 部分key所在的槽位,分布于新老node。

  2. 若key在老node,则直接返回给客户端。

  3. 若key不在老node,存在两种可能,key在新节点或者key根本就不存在,老节点不知道属于哪种情况,只能回ASK targetNodeAddr。

  4. 客户端收到ASK targetNodeAddr后,先去目标node执行不带参数的asking指令,然后再执行原先的操作指令,为何这么做呢?因为还没有迁移完,目标节点不认,可能会再次重定向到源节点,形成死循环,不带参数的asking命令告诉目标node节点,要当成自己的槽位进行处理,若找到对应key则返回给客户端,若没有找到,则告知客户端没有此key。

25. Redis组件集群的几种方式

  1. 客户端分片:Redis的java客户端jedis支持分片,采用一致性hash,优点是服务端各Redis实例之间无感知,可以线性扩容;缺点是不能动态扩容或者缩容,每个客户端都需要更改配置,运维麻烦,可能存在资源浪费。

  2. 基于代理的分片:例如,Codis,优点:客户端无感知,和访问单实例Redis一样,缺点:部分命令不支持。

  3. 路由查询,Redis-cluster,官方集群方式,无中心化节点,每个节点都保存集群的所有信息和状态,和其他节点通过Gossip进行通信。用槽位分配方法,没有用一致性hash,每个节点都要正常工作,否则集群无法对外服务(cluster-require-full-coverage 可以允许部分节点故障,其他节点还可以继续提供服务),每个节点都需要主从配置,当主节点故障后,自动将从节点提升为主节点。

26. Redis的安全

Redis不支持SSL链接,客户端和服务端的交互应该在内网进行,若必须跨越公网,官方推荐spiped。在客户端和服务端各起一个spiped进程监听,负责各自的数据传输,成对出现的spiped进程,共享相同的加密秘钥,spiped支持多个客户端的数据转发,但是每个server节点必须安装一个spiped进程。

Redis基础知识总结(面试必备)

27. Redis的info指令

  • redis-cli info stats |grep ops : 查看qps。

  • redis-cli monitor , 查看执行的指令。

  • redis-cli info clients :查看连接的客户端数量。

  • redis-cli client list: 查看客户端列表。

  • redis-cli info stats | grep rejected_connections : 查看拒绝的连接数,若过大,说明需要调整maxclients参数。

  • redis-cli info memory :查看内存使用状况。

  • redis-cli info replication:查看复制区域信息, 其中,repl_backlog_size很重要,如果设置过小,主从断开期间主节点的修改指令过多,会将缓冲区(环形的)写满覆盖,从而导致从机重新连接上来后发起全量同步,要根据业务情况合理设置,最好不会被写满,这样从机上来后可以只发起增量同步。根据info stats | grep sync输出的sync_partial_err指标,半同步失败次数, 通过这个数值来决定是否需要调整积压缓冲区。

28. Redis事务样例代码

#-*- coding:utf-8

import redis

import time

import threading

locks = threading.local()

locks.redis = {}


client = redis.Redis(host='localhost', port=6379, decode_responses=True)



pool = redis.ConnectionPool(host='localhost', port=6379, decode_responses=True)

client2 = redis.Redis(connection_pool=pool)

client2.set('gender', 'male')


def key_for(user_id):

return "account_{}".format(user_id)


def double_account(client, user_id):

key = key_for(user_id)

while True:

client.watch(key)

value = int(client.get(key))

value *= 2

pipe = client.pipeline(transaction=True)

pipe.multi()

pipe.set(key, value)

try:

time.sleep(10)

ret = pipe.execute()

print("ret=" + str(ret))

print("succ")

break;

except redis.WatchError:

print("fail")

continue

return int(client.get(key))


user_id="abc"

client2.setnx(key_for(user_id), 5)

print (double_account(client2, user_id))

29. Redis限流样例代码

#-*- coding:utf-8

import time


class Funnel(object):

def __init__(self, capacity, leaking_rate):

self.capacity = capacity

self.leaking_rate = leaking_rate

self.left_quota = capacity

self.leaking_ts = time.time()


def make_space(self):

now_ts = time.time()

delta_ts = now_ts - self.leaking_ts

delta_quota = delta_ts * self.leaking_rate

if delta_quota < 1 :

return

self.left_quota += delta_qota

self.leaking_ts = now_ts

if self.left_quota > self.capacity:

self.left_quota = sefl.capacity


def watering(self, quota):

self.make_space()

if self.left_quota > quota:

self.left_quota -= quota

return True

return False


funnels = {}

def is_action_allowed(user_id, action_key, capacity, leaking_rate):

key="%s:%s"%(user_id, action_key)

funnel = funnels.get(key)

if not funnel:

funnel = Funnel(capacity, leaking_rate)

funnels[key] = funnel

return funnel.watering(1)


for i in range(1000):

print(is_action_allowed('laoqian', 'reply', 15, 0.5))

30. 可重入锁样例代码

#-*- coding:utf-8

import redis

import threading

locks = threading.local()

locks.redis = {}

def key_for(user_id):

return "account_{}".format(user_id)


def _lock(client, key):

ret = client.set(key, "123", nx=True, ex=5)

return ret


def _unlock(client, key):

client.delete(key)


def lock(client, user_id):

key= key_for(user_id)

if key in locks.redis:

locks.redis[key] += 1

return True

ok = _lock(client, key)

if not ok:

return False

locks.redis[key] = 1

return True


def unlock(client, user_id):

key = key_for(user_id)

if key in locks.redis:

locks.redis[key] -= 1

if locks.redis[key] <= 0:

del locks.redis[key]

_unlock(client, key)

return True

return False


client = redis.Redis(host='localhost', port=6379, decode_responses=True)

print("lock:" + str(lock(client, "codehole")))

print("lock:" + str(lock(client, "codehole")))

print("unlock:" + str(unlock(client, "codehole")))

print("unlock:" + str(unlock(client, "codehole")))


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

共享经济时代

共享经济时代

雷切尔·博茨曼、路·罗杰斯 / 唐朝文 / 上海交通大学出版社 / 2015-6-1 / 38

“共享经济”(sharing economy),也被称为“协同消费”(collaborative consumption),是在互联网上兴起的一种全新的商业模式。简单地说,消费者可以通过合作的方式来和他人共同享用产品和服务,而无需持有产品与服务的所有权。使用但不拥有,分享替代私有,即“我的就是你的”。 当下,全球经济正呈现出这样一种前所未有的趋势:消费者之间的分享、交换、借贷、租赁等共享经济......一起来看看 《共享经济时代》 这本书的介绍吧!

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

URL 编码/解码
URL 编码/解码

URL 编码/解码

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具