1.redis是key-value的数据结构 每条数据都是一个键值对 键的类型是字符串,并且不能重复 值的数据类型分为五种:string、hash、list、set、zset
2.数据库没有名字,默认有16个库,通过0-15来标识连接redis数据库。默认选择第一个数据库,即0
可以使用OBJECT ENCODING key来查看某个数据类型的底层实现
哈希算法如下:
// 1、使用字典设置的哈希函数,计算键 key 的哈希值 hash = dict->type->hashFunction(key); // 2、使用哈希表的sizemask属性和第一步得到的哈希值,计算索引值 index = hash & dict->ht[x].sizemask;跳跃表具有以下性质:
由很多层结构组成;每一层都是一个有序的链表,排列顺序为由高层到底层,都至少包含两个链表节点,分别是前面的head节点和后面的nil节点;最底层的链表包含了所有的元素;如果一个元素出现在某一层的链表中,那么在该层之下的链表也全都会出现(上一层的元素是当前层的元素的子集);链表中的每个节点都包含两个指针,一个指向同一层的下一个链表节点,另一个指向下一层的同一个链表节点;压缩列表的每个节点构成如下:
previous_entry_length用于反向遍历 encoding保存的是节点的content的内容类型以及长度,encoding类型一共有两种,字节数组和整数,encoding区域长度为1字节、2字节或者5字节长
string、hash、list、set、zset
可以使用type key来查看对应value的类型
Redis中的每个对象都是由redisObject结构来表示
typedef struct redisObject{ //类型(五大数据类型) unsigned type; //编码(底层实现的数据类型) unsigned encoding; //指向底层数据结构的指针 void *ptr; //引用计数 int refcount; //记录最后一次被程序访问的时间 unsigned lru; }robj其中type和encoding的对应关系如下:
字符串对象的编码可以是int,raw或者embstr:
int编码:保存的是可以用long类型表示的整数值;raw编码:保存长度大于44字节的字符串(redis3.2版本之前是39字节,之后是44字节);embstr编码:保存长度小于44字节的字符串(redis3.2版本之前是39字节,之后是44字节)。当同时满足下面两个条件时,使用ziplist(压缩列表)编码:
列表保存元素个数小于512个;每个元素长度小于64字节; 不能满足这两个条件的时候使用linkedlist(双端链表)编码。 上面两个条件可以在redis.conf配置文件中的list-max-ziplist-value选项和list-max-ziplist-entries选项进行配置。哈希对象的编码可以是ziplist或者dictht,具体规则与list类似
集合对象的编码可以是intset或者dictht
当集合同时满足以下两个条件时,使用intset编码:
集合对象中所有元素都是整数集合对象所有元素数量不超过512 不能满足这两个条件的就使用dictht编码。第二个条件可以通过配置文件的set-max-intset-entries进行配置。有序集合的编码可以是ziplist或者skiplist
一个zset结构同时包含一个字典和一个跳跃表,字典的键保存元素的值,字典的值则保存元素的分值;跳跃表节点的object属性保存元素的成员,跳跃表节点的score`属性保存元素的分值。这两种数据结构会通过指针来共享相同元素的成员和分值,所以不会产生重复成员和分值,造成内存的浪费。
当有序集合对象同时满足以下两个条件时,对象使用ziplist编码:
保存的元素数量小于128;保存的所有元素长度都小于64字节; 不能满足上面两个条件的使用skiplist编码。以上两个条件也可以通过配置文件的zset-max-ziplist-entries选项和zset-max-ziplist-value进行修改优点:
直接二进制dump,文件紧凑,体积小;恢复快;缺点:
只能全量持久化,一次耗时较长;二进制,基本没有可读性和可修改性。优点:
记录所有更新日志,可读性和可修改性强;支持持续无间断备份;缺点:
文件体积大,恢复慢;比较影响整体性能。当 Redis 内存超出物理内存限制时,内存的数据会开始和磁盘产生频繁的交换 (swap)。交换会让 Redis 的性能急剧下降,对于访问量比较频繁的 Redis 来说,这样龟速的存取效率基本上等于不可用。 在生产环境中我们是不允许 Redis 出现交换行为的,为了限制最大使用内存,Redis 提供了配置参数 maxmemory 来限制内存超出期望大小。 当实际内存超出 maxmemory 时,Redis 提供了几种可选策略 (maxmemory-policy) 来让用户自己决定该如何腾出新的空间以继续提供读写服务。
(volatile策略只会针对带过期时间的 key 进行淘汰,allkeys策略会对所有的 key 进行淘汰) 1.noeviction:不会继续服务写请求 (DEL 请求可以继续服务),读请求可以继续进行。这样可以保证不会丢失数据,但是会让线上的业务不能持续进行。这是默认的淘汰策略。 2.volatile-lru:尝试淘汰设置了过期时间的 key,最少使用的 key 优先被淘汰。没有设置过期时间的 key 不会被淘汰,这样可以保证需要持久化的数据不会突然丢失。 3.volatile-ttl:跟上面一样,除了淘汰的策略不是 LRU,而是 key 的剩余寿命 ttl 的值,ttl 越小越优先被淘汰。 4.volatile-random:跟上面一样,不过淘汰的 key 是过期 key 集合中随机的 key。 5.allkeys-lru 区别于 volatile-lru,这个策略要淘汰的 key 对象是全体的 key 集合,而不只是过期的 key 集合。这意味着没有设置过期时间的 key 也会被淘汰。 6.allkeys-random 跟上面一样,不过淘汰的策略是随机的 key。
1.缓存穿透: 用户想要查询一个数据,发现redis内存数据库没有,也就是缓存没有命中,于是向持久层数据库查询。发现也没有,于是本次查询失败。一般通过布隆过滤器、缓存空对象来解决
2.缓存击穿:一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库。一般通过设置热点数据永远不过期、加互斥锁来解决
3.缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机。一般通过过期时间设置随机、数据预热来解决