目录
redis单线程模型
IO 多路复用机制同时监听多个 socket,将产生事件的 socket 压入内存队列中,事件分派器根据 socket 上的事件类型来选择对应的事件处理器进行处理。
客户端与redis通信的一次流程
redis数据结构
必看:Redis详解(四)------ redis的底层数据结构 - YSOcean - 博客园
Redis详解(五)------ redis的五大数据类型实现原理 - YSOcean - 博客园
简介:redis的五种数据结构原理分析_至臻于勤-大道至简的博客-优快云博客
数据结构:Redis详解(四)------ redis的底层数据结构 - YSOcean - 博客园
String
命令:
SET hello 你好;get hello
简单动态字符串(simple dynamic string,SDS), value 最多可以是 512M,string 可以包含任何类型数据
1、len 保存了SDS保存字符串的长度
2、buf[] 数组用来保存字符串的每个元素,二进制存储
3、free 记录了 buf 数组中未使用的字节数
优点:
直接获取字符串长度,无序挨个遍历
杜绝缓冲区溢出(扩展字符串时,通过len进行判断是否有足够的长度)
减少修改字符串的内存重新分配次数(字符串扩展时多分配些空间,回收时把回收的字节长度记录在free中)
二进制安全可以存放图片(C语言原生字符串是以\n结尾,\n可能存在于图片二进制数据中。SDS不是以空判断是否结束而是以len)
List(链表)
命令:rpush list1 "bar";lpop list1
quicklist=ziplist+linkedlist(双向列表)
每个quicklist节点就是一个ziplist,具备压缩列表的特性
Set(集合)
命令:SADD myset "hello"
数据结构:intset+hashtable
使用散列表(hashtable)来保证自已存储的每个字符串都是各不相同的,且无序。
intset(有序不重复整数数组)
Redis用于保存有序不重复整数值的集合抽象数据类型,当一个集合只包含数量不多整数值元素时, Redis 就会使用整数集合作为集合键的底层实现
1、存储有序整数集合,存放同一类型的整数(三种整数:int16_t、int32_t、int64_t)
2、例如:原来是int16_t的集合,当插入int32_t的整数的时候就会为每个元素升级为int32_t,这时候会对内存重新分配。不能降级。
typedef
struct
intset{
//编码方式
uint32_t encoding;
//集合包含的元素数量
uint32_t length;
//保存元素的数组
int8_t contents[];
}
ZSet(有序集合)
命令:ZADD KEY_NAME SCORE1 VALUE1.. SCOREN VALUEN
1、键被称为成员(member),每个成员都是各不相同的 值则被称为分值(score),分值必须为浮点数。
2、可通过成员访问元素,也可通过分值以及分值的排列顺序访问元素的结构
3、数据结构:ziplist和跳表
Hash
命令:HSET myhash field1 "foo"
1、基于原表新建表,计算索引值,然后将键值对放到新的哈希表位置上,释放原哈希表的内存空间,扩容2n,收缩n/2
2、渐近式 rehash,数据量大时,会同时保存两个hash表。
3、字典底层使用哈希表实现,每个字典通常有两个哈希表,一个平时使用,另一个用于rehash时使用,使用链地址法解决哈希冲突。
1、ziplist
- 当键的个数小于hash-max-ziplist-entries(默认512)
- 当所有值都小于hash-max-ziplist-value(默认64)
2、hashtable(哈希表)
ziplist
压缩列表(ziplist)是Redis为了节省内存而开发的,是由一系列特殊编码的连续内存块组成的顺序型数据结构,一个压缩列表可以包含任意多个节点(entry),每个节点可以保存一个字节数组或者一个整数值。
压缩列表的原理:压缩列表并不是对数据利用某种算法进行压缩,而是将数据按照一定规则编码在一块连续的内存区域,目的是节省内存。
1、经过特殊编码的双向链表,目的是提高存储效率,用于存储字符串和整数,整数是按二进制表示进行编码的。
2、普通双向链表的每一项占用单独的内存,会带来大量内存碎片,ziplist将表中的每一项存放在前后连续的地址空间内。
3、值的存储采用变长的编码方式(大的整数多用一些字节存储,小的整数少用一些字节存储)
①、previous_entry_ength:记录压缩列表前一个字节的长度。previous_entry_ength的长度可能是1个字节或者是5个字节,如果上一个节点的长度小于254,则该节点只需要一个字节就可以表示前一个节点的长度了,如果前一个节点的长度大于等于254,则previous length的第一个字节为254,后面用四个字节表示当前节点前一个节点的长度。利用此原理即当前节点位置减去上一个节点的长度即得到上一个节点的起始位置,压缩列表可以从尾部向头部遍历。这么做很有效地减少了内存的浪费。
②、encoding:节点的encoding保存的是节点的content的内容类型以及长度,encoding类型一共有两种,一种字节数组一种是整数,encoding区域长度为1字节、2字节或者5字节长。
③、content:content区域用于保存节点的内容,节点内容类型和长度由encoding决定。
缓存雪崩、缓存穿透、缓存击穿
缓存雪崩:redis服务器挂掉导致请求大量涌至数据库;
缓存穿透:大量缓存中不存在的请求key访问直接落到数据库,一般是恶意攻击;
最常见的则是采用布隆过滤器,
缓存击穿:热点key在请求高峰失效,瞬间大量请求落到数据库;我理解的是这样
1、查询数据库前添加分布式锁
2、接口限流与熔断、降级
3、布隆过滤器
3、随机过期时间
4、做二级缓存,或者双缓存策略。A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为短期,A2设置为长期。
5、缓存预热
Redis缓存穿透、缓存雪崩问题分析_饭一碗的博客-优快云博客_redis缓存雪崩
RDB和AOF
AOF 是redis的一种持久化方式,用来记录所有的写操作,但是随着时间增加,aof文件会越来越大,所以需要进行重写,将内存中的数据重新以命令的方式写入aof文件,新生成一个文件,内存数据写入文件后进行rename操作,从而用新的aof文件替换老文件。可设置超过原aof文件的多少倍时进行重写新文件。
在重写的过程中,由于redis还会有新的写入,为了避免数据丢失,会开辟一块内存用于存放重写期间产生的写入操作,等到重写完毕后会将这块内存中的操作再追加到aof文件中。这块内存就是内存缓存区。
持久化和工作原理:搞懂Redis RDB和AOF持久化及工作原理 - GrimMjx - 博客园
同时开启听谁的:黑猴子的家:Redis 持久化 之 AOF 和 RDB 同时开启,Redis听谁的? - 简书
aof重写时导致OOM:https://my.oschina.net/feicuigu/blog/1798122
为什么要进行重写:Redis之AOF重写及其实现原理_叶戈尔的博客-优快云博客_bgrewriteaof原理
redis主从复制
redis主从复制原理和介绍_sam-123的博客-优快云博客_redis主从复制解决什么问题
redis实战--redis主从复制实现读写分离(原理)_Jason_hsu2017的博客-优快云博客_redis读写分离实战
redis事务
基础面试题
redis面试题:几率大的Redis面试题(含答案)__睶_的博客-优快云博客_redis面试题
史上最全50道Redis面试题(含答案),以后面试再也不怕问Redis了 - 码农教程
2018整理最全的50道Redis面试题!_剑雪封喉r的博客-优快云博客_redis面试题
redis实现分布式锁
结论:
加锁:set lock 32ddgwgn22r8 30 s
1、加超时时间?
是防止死锁
2、加唯一key?
防止误把别的线程的锁删除了。如:线程1锁超时,线程2获取锁成功,线程1执行完后就把线程2的锁删了。
3、原子?
设置完锁后,服务器出现问题,导致超时时间没设置上。导致死锁(没有线程能获取到该锁)。
解锁:lua脚本原子操作,get(key) == value 就删除锁,否则不删。
1、 原子?
线程1判断正确后,此时过了分布式锁超时时间,线程2拿到锁,然后线程1就把线程2的锁删了。