最近学习了一段时间的redis,以下是笔记,欢迎各位大佬们留言评论,若有不对的地方请指出,我会及时更正!蟹蟹大家!
目录:
1.redis简介
2.redis丰富的数据结构
3.redis事务、分布式锁
4.持久化
5.reids存储使用的数据模型
6.redis淘汰策略
7.redis底层数据类型介绍
redis简介
基于内存存储的非关系型数据库、
性能强劲、单线程运作、请求入队
支持的数据结构丰富:string、list、hash、set、zset
支持RDB和AOF持久化
redis支持的数据结构:string
set key value
setnx key value
setex key seconds value
exists key
get key
mget key1 key2 …
mset k1 v1 k2 v2 …
incr key
getset key value
pttl key
.....................................
redis支持的数据结构:hash
hset key field value
hsetnx key value 如果field存在则不改变
hget key field
hdel key fidle1 field2 …
hlen key 返回字段个数
hmget key f1 f2 …
hmset key f1 v1 f2 v2 …
hexists key field
hkeys key
hvals key
hgetall key
hincrby key field
hincrbyfloat key
field
..............................................
Ps:哈希类型的内部编码有ziplist 和 hashtable
当哈希元素小于hash-max-ziplist-entries配置、同时所有值都小于hash-max-ziplist-value配置,就会利用ziplist这样更节省内存 /etc/redis/redis.conf
object encoding key 可以查看数据的编码,我们可以发现,当超过上述两个配置的值后,编码会发生改变
redis支持的数据结构:list
lpush key v1 v2 …
rpush key v1 v2 …
lrange start end 0 -1查找所有
linsert key before/after value
lindex key index -1返回最后一个
llen key
lpop key
rpopkey
lremkey count value
…………………........................
Ps:内部编码有ziplist 和 linkedlist
当list元素小于list-max-ziplist-entries配置、同时所有值都小于list-max-ziplist-value配置,就会利用ziplist这样更节省内存
Redis3.2版本新增了quicklist编码,以ziplist为节点的linkedlist,结合了两者的优势
redis支持的数据结构:set
set:无索引、无重复、无序
sadd key v1 v2 …
srem key v1 v2 …
scard key 计算元素个数
sismember key element 判断元素是否在集合中
spop key 随机弹出元素
smembers key 返回所有元素
sinter key1 key2 返回两个集合交集
sunion key1 key2 返回两个集合并集
sdiff key1 key2 返回两个集合差集,顺序影响结果
…………………..................
Ps:内部编码有intset 和 hashtable
当set集合元素小于set-max-intset-entries配置默认512个且都是整数,就会利用intset这样更节省内存,当数量没有达到,但是其中只要有一个元素不是整数,编码都会变为hashtable
redis支持的数据结构:zset
zset:元素有分数作为排序依据、无重复
zadd key score member …
zcard key 计算成员个数
zscore key member 返回这个成员分数
zrank key member 从低到高返回排名
zrevrank key member 从高到低返回排名,从0开始算的
zrem key member 移除元素
zincrby key increment member 给元素增加分数
zrange key start end 返回排名的成员从低到高
zrevrange key start end 返回排名的成员反之
zcount key min max 返回指定分数范围元素个数
zremrangebyscore key min max 删除指定分数范围成员
…………………..................................
Ps:内部编码有ziplist 和 skiplist
当zset集合元素个数小于zset-max-ziplist-entries配置默认128,且每个元素的值都小于zset-max-ziplist-value默认64字节,就会使用ziplist编码,否则编码都会变为skiplist,因为超过设置值,读写效率就会下降
redis事务
1.multi 事务开始
2.一个事务需要执行的操作,例如多条命令行
3.exec结束事务;discard取消事务
redis事务不支持回滚,只要事务开始到事务提交,期间只要有命令执行成功,就会改变,所以redis提供了watch命令解决这个问题
1.watch key
2.multi
3.命令…
4.exec提交事务
watch代表监听一个key,执行事务的时候如果这个key发生了改变,事务执行就不成功,返回nil,这个时候需要人工干预
redis分布式锁
这些命令是分布式锁需要用到的命令,具体的流程如图所示
setnx key value 返回1则代表拿到锁,返回0则代表没有拿到锁
expire key timeout 为key设置一个超时时间,防止出现锁永远不被释放的情况
del key 释放锁
getset key
value
redis持久化
rdb
RDB持久化是把当前进程数据生成快照保存到硬盘的过程,触发RDB持久化过程分为手动触发和自动触发。
手动:save bgsave,执行save命令的时候会阻塞redis直到RDB完成为止;bgsave则是fork出子线程完成RDB操作,不会阻塞redis,只是在fork子线程会阻塞,这个时间不会很长;
自动:在配置文件中有默认save 900 1 save 300 10 save 60 10000,只要满足这三个其中的任意一个都会执行自动bgsave;
redis的save m n 是通过serverCron,dirty,lastsave时间戳来实现的;
serverCron检查配置文件、
dirty计数器记录数据库一共执行了多少次修改,执行bgsave会置零
lastsave是记录上次执行bgsave的时间
当前时间-lastsave>m&&dirty>=n
RDB文件载入工作是在服务器启动时自动执行,AOF优先级高于RDB,所以如果要使用RDB就要关闭AOF,redis载入RDB文件期间,redis处于阻塞状态,直到载入完成为止;
aof
AOF持久化是将每次redis执行的命令记录起来,类似于mysqlbinlog,redis重启时执行aof文件中的命令来恢复数据
配置文件开启appendonly yes ,aof是自动记录,aof执行流程;
1.命令追加:将命令追加到缓冲区aof_buf,这是为了避免磁盘io导致性能降低;
2.文件写入和文件同步:缓冲区的文件同步策略参数由appendfsync控制
Everysec,默认,每次的命令都写入aof文件中,但是每秒执行一次文件同步操作;
Always:每次的命令写入aof文件中,并且执行文件同步操作,速度最慢但是最安全;
No:每次命令写入aof文件中,但是由操作系统决定何时执行文件同步,速度最快但是不安全;
3.文件重写:子线程执行,不影响主线程,目的主要是消除重复命令,过期的数据,减少aof文件的大小;
redis数据模型
涉及到内存分配器(如jemalloc)、简单动态字符串(SDS)、5种对象类型及内部编码、redisObject。
下图是执行set hello world时,涉及到的数据模型
dictEntry:每个键值对都会有一个dictEntry,里面存储了指向key和value的指针,next指向下一个dictEntry;
Key:并不是直接存储hello,而是存储在sds结构中;
Value:redisObject,redis支持的五种数据类型都是通过redisObject来存储的,type代表类型,ptr指向对象所在地址,字符串类型的值虽然通过redisObject包装,但是仍然通过sds来存储;
Jemalloc:redis内存分配器有三种,libc、jemalloc、tcmalloc,默认是jemalloc,在64位系统中,将内存分为,小,大,巨大三个级别,每个级别又有很多区域,redis存储数据时,会找最合适的区域来存储;
RedisObject:类似于这样的属性RedisObject(type, lru, redis, refcount, encoding)
type,表示对象的类型,也就是redis支持的数据结构,占4个比特;
encoding,表示对象的内部编码,占4个比特;
lru,记录了对象最后一次被命令访问的时间,对比这个时间和当前时间可以得出这个对象的空转,从而在redis内存回收机制是-lru的时候,会优先选择空转时间最长的对象进行释放,4.0版本占24比特,2.6版本占22比特;
refcount,表示该对象被引用的次数,整型,初始化系对象为1,被新的调用,+1,当对象不再被新的调用,-1,如果为0,则释放该对象,refcount>1被称为共享对象,因为出现重复的对象,redis不会重复创建;
ptr,指向具体的数据,比如字符串就指向sds;
SDS:简单动态字符串(simple dynamic string),SDS(len, free, buf)
Len:表示buf已经使用的长度
Free:表示buf未使用的长度
Buf:字节数组
与C字符串类似的是,buf也是以’\0’作为结尾;
redis淘汰策略
noeviction: 不删除策略, 达到最大内存限制时, 如果需要更多内存, 直接返回错误信息。
allkeys-lru: 所有key通用; 优先删除最近最少使用(less recently used ,LRU) 的 key。
volatile-lru: 只限于设置了 expire 的部分; 优先删除最近最少使用(less recently used ,LRU) 的 key。
allkeys-random: 所有key通用; 随机删除一部分 key。volatile-random: 只限于设置了 expire 的部分; 随机删除一部分 key。
volatile-ttl: 只限于设置了 expire 的部分; 优先删除剩余时间(time to live,TTL) 短的key。
redis简单正则查询key
合理的运用一些正则符号,比如 * ? [] 减少去全量查找产生的内存开销
keys hell* 可以查到到比如 hello helloo *代表一位或者多位
keys hell? 可以查找到比如 hello hella ?代表一位
keys hel[lo]o可以查到hello heloo []里面填的什么内容就会在这个数组里面匹配,里面也可以放正则表达式,比如[a-zA-Z]
redis数据类型详解
linkedList:
linkedList:双向列表的每个数据节点都有两个指针,分别指向前面一个节点和后一个节点,因此从双向列表中的每一个节点都很容易获取前一个节点和后一个节点;
这个是linkedList的数据结构
特点:访问快速,从头部或者尾部插入元素快,时间复杂度为o(1),自带链表长度的属性,所以得出链表长度的时间复杂度为o(1);
zipList:
适用于存储小的整数值或者比较简短的字符串,如果超过redis的配置,会自动转换为linkedlist;
之所以达到了压缩的效果,是因为存储的原理避开了内存碎片,它占用的是一大块内存,每一个entry都是连续再一起的,而linkedlist是通过指针来指向的,首先指针会占内存,内存分配器分配内存的时候并不是数据多大就分配多大内存,而是分配一块合适这个数据的内存空间,也造成了内存浪费;
压缩的原理:entry里面的prevrawlen属性会记录前一个entry占用的字节总数,如果前一个entry的长度小于254字节,那么prevrawlen属性就会用1个字节的空间保存这个长度值,反之上一个entry大于等于254,就会用5个字节保存这个值;
弊端:连锁更新效应,假设现在有一个ziplist,有10个entry,每个entry的大小都是253,那么每个prevrawlen的都是占用1个字节,但是我现在插入了一条255的entry,这个时候新entry的后一个entry的prevrawlen属性就需要扩大,导致这个entry的大小变了,又导致后面的也变了,一直变到最后一个entry,这样子就一直连续在做内存分配操作,时间复杂度大大提升,最坏的情况是o(N^2)
intset:
创建一个set集合的时候,首先会指定一个默认编码,默认是INTSET_ENC_INT16
执行插入新元素的时候,会根据value得到合适的编码,然后比较当前集合的编码,如果不满足就对集合进行升级;
如果满足,先判断集合元素是否存在,如果存在就直接返回,如果不存在,扩大集合空间,选新的内存地址,添加新元素;
intset内部只有”编码升级”的过程,没有”降级”的操作。当将唯一一个高位元素从将集合移除时,此时,集合不会转换为低位编码集合。
hashtable:
table属性是一个数组,里面存储的都是一个指向dictEntry的指针,而每个dictEntry都有一个next指向下一个元素的指针属性,如果出现键哈希值相同的情况,会被分到同一个索引上,然后它们通过next就形成了一个单向链表,从而解决了哈希冲突的问题;
Set应用场景:它的特性是无序,不重复,求交集、并集、差集很方便;适用于存储用户粉丝,计算共同好友;