redis
2023-01-10 11:42:15 2 举报
AI智能生成
redis总结
作者其他创作
大纲/内容
数据类型
字符串(strings)
散列(hashes)
列表(lists)
集合(sets)
有序集合(sorted sets)
位图(bitmaps)
通过最小的单位bit来进行0或者1的设置,表示某个元素对应的值或者状态
基数(hyperloglogs)
计算重复的值,确定存储的数量,只提供基数的运算,不提供返回的功能
地理空间(geospatial)
可以推算出地理位置,两地之间的距离
常用命令
strings
SET
将key设定为指定字符串值,如果key已经保存了一个值,将会覆盖原来的值,并且忽略原来类型,当set执行成功后,之前设置的过期时间都会失效
SET EX 设置过期时间,单位秒,eg:set mykey myvalue EX 60,代表60秒后过期
SET PX 设置键key的过期时间,单位时毫秒
SET NX 只有键key不存在的时候才会设置key的值 eg:seg mykey myvalue2 NX 如果已经存在值,则不会生效
SET XX 只有键key存在的时候才会设置key的值 eg:seg mykey myvalue2 NX 如果不存在值,则不会生效
GET
获取key的值,如果value不是string,则返回错误,get只处理string类型的的value
INCR
字符串操作,执行原子加1,如果指定的key不存在,那么在执行incr操作之前,会先将它的值设定为0
执行这个操作的时候,key对应存储的字符串被解析为10进制的64位有符号整型数据
DECR
整数原子减一
SETBIT
bit要么被设置,要么被清空,这个由value(只能是0或者1)来决定。当key不存在的时候,就创建一个新的字符串value
eg:SETBIT mykey 7 1
GETBIT
返回key对应的string在offset处的bit值 当offset超出了字符串长度的时候,这个字符串就被假定为由0比特填充的连续空间。当key不存在的时候,它就认为是一个空字符串,所以offset总是超出范围,然后value也被认为是由0比特填充的连续空间
eg:GETBIT mykey 7
hashs
HSET
设置 key 指定的哈希集中指定字段的值。
如果 key 指定的哈希集不存在,会创建一个新的哈希集并与 key 关联。
如果字段在哈希集中存在,它将被重写
如果 key 指定的哈希集不存在,会创建一个新的哈希集并与 key 关联。
如果字段在哈希集中存在,它将被重写
返回结果:1 代表是一个新的字段,0:代表原来的map里面已经存在,覆盖了原来值
eg:HSET myhash field1 "Hello"
HGET
返回 key 指定的哈希集中该字段所关联的值
eg:HSET myhash field1
HDEL
从 key 指定的哈希集中移除指定的域。在哈希集中不存在的域将被忽略。
如果 key 指定的哈希集不存在,它将被认为是一个空的哈希集,该命令将返回0
如果 key 指定的哈希集不存在,它将被认为是一个空的哈希集,该命令将返回0
HLEN
返回 key 指定的哈希集包含的字段的数量
HVALS
返回 key 指定的哈希集中所有字段的值
Lists
LINSERT
把 value 插入存于 key 的列表中在基准值 pivot 的前面或后面。
当 key 不存在时,这个list会被看作是空list,任何操作都不会发生。
当 key 存在,但保存的不是一个list的时候,会返回error。
当 key 不存在时,这个list会被看作是空list,任何操作都不会发生。
当 key 存在,但保存的不是一个list的时候,会返回error。
eg:
RPUSH mylist "Hello"
RPUSH mylist "World"
LINSERT mylist BEFORE "World" "There"
RPUSH mylist "Hello"
RPUSH mylist "World"
LINSERT mylist BEFORE "World" "There"
LPUSH
将所有指定的值插入到存于 key 的列表的头部。如果 key 不存在,那么在进行 push 操作前会创建一个空列表。 如果 key 对应的值不是一个 list 的话,那么会返回一个错误
EG:LPUSH mylist "world"
RPUSH
向存于 key 的列表的尾部插入所有指定的值。如果 key 不存在,那么会创建一个空的列表然后再进行 push 操作。 当 key 保存的不是一个列表,那么会返回一个错误
LRANGE
返回存储在 key 的列表里指定范围内的元素。 start 和 end 偏移量都是基于0的下标,即list的第一个元素下标是0(list的表头),第二个元素下标是1,以此类推。
偏移量也可以是负数,表示偏移量是从list尾部开始计数。 例如, -1 表示列表的最后一个元素,-2 是倒数第二个,以此类推
偏移量也可以是负数,表示偏移量是从list尾部开始计数。 例如, -1 表示列表的最后一个元素,-2 是倒数第二个,以此类推
eg:LRANGE mylist 0 0
LPUSHX
只有当 key 已经存在并且存着一个 list 的时候,在这个 key 下面的 list 的头部插入 value。 与 LPUSH 相反,当 key 不存在的时候不会进行任何操作
RPUSHX
将值 value 插入到列表 key 的表尾, 当且仅当 key 存在并且是一个列表。 和 RPUSH 命令相反, 当 key 不存在时,RPUSHX 命令什么也不做
LPOP
移除并且返回 key 对应的 list 的第一个元素
eg:RPOP mylist
RPOP
移除并返回存于 key 的 list 的最后一个元素
RPOPLPUSH
原子性地返回并移除存储在 source 的列表的最后一个元素(列表尾部元素), 并把该元素放入存储在 destination 的列表的第一个元素位置(列表头部)。
例如:假设 source 存储着列表 a,b,c, destination存储着列表 x,y,z。 执行 RPOPLPUSH 得到的结果是 source 保存着列表 a,b ,而 destination 保存着列表 c,x,y,z。
如果 source 不存在,那么会返回 nil 值,并且不会执行任何操作。 如果 source 和 destination 是同样的,那么这个操作等同于移除列表最后一个元素并且把该元素放在列表头部, 所以这个命令也可以当作是一个旋转列表的命令
例如:假设 source 存储着列表 a,b,c, destination存储着列表 x,y,z。 执行 RPOPLPUSH 得到的结果是 source 保存着列表 a,b ,而 destination 保存着列表 c,x,y,z。
如果 source 不存在,那么会返回 nil 值,并且不会执行任何操作。 如果 source 和 destination 是同样的,那么这个操作等同于移除列表最后一个元素并且把该元素放在列表头部, 所以这个命令也可以当作是一个旋转列表的命令
EG:RPOPLPUSH mylist myotherlist
"three"
"three"
LINDEX
返回列表里的元素的索引 index 存储在 key 里面。 下标是从0开始索引的,所以 0 是表示第一个元素, 1 表示第二个元素,并以此类推。 负数索引用于指定从列表尾部开始索引的元素。在这种方法下,-1 表示最后一个元素,-2 表示倒数第二个元素,并以此往前推。
当 key 位置的值不是一个列表的时候,会返回一个error
当 key 位置的值不是一个列表的时候,会返回一个error
EG:LINDEX mylist 0
LSET
设置 index 位置的list元素的值为 value
当index超出范围时会返回一个error
当index超出范围时会返回一个error
EG:LSET mylist 0 "four"
LLEN
返回存储在 key 里的list的长度。 如果 key 不存在,那么就被看作是空list,并且返回长度为 0。 当存储在 key 里的值不是一个list的话,会返回error
SETS
SADD
添加一个或多个指定的member元素到集合的 key中.指定的一个或者多个元素member 如果已经在集合key中存在则忽略.如果集合key 不存在,则新建集合key,并添加member元素到集合key中.
如果key 的类型不是集合则返回错误
如果key 的类型不是集合则返回错误
eg:SADD myset "Hello"
SMEMBERS
返回key集合所有的元素.
该命令的作用与使用一个参数的SINTER 命令作用相同.
该命令的作用与使用一个参数的SINTER 命令作用相同.
SINTER
返回指定所有的集合的成员的交集
SISMEMBER
返回成员 member 是否是存储的集合 key的成员
SPOP
从存储在key的集合中移除并返回一个或多个随机元素。
此操作与SRANDMEMBER类似,它从一个集合中返回一个或多个随机元素,但不删除元素
此操作与SRANDMEMBER类似,它从一个集合中返回一个或多个随机元素,但不删除元素
SREM
在key集合中移除指定的元素. 如果指定的元素不是key集合中的元素则忽略 如果key集合不存在则被视为一个空的集合,该命令返回0.
如果key的类型不是一个集合,则返回错误
如果key的类型不是一个集合,则返回错误
sorted sets
数据结构:有序集合的分数使用双精度64位浮点数
ZADD
将所有指定成员添加到键为key有序集合(sorted set)里面。 添加时可以指定多个分数/成员(score/member)对。 如果指定添加的成员已经是有序集合里面的成员,则会更新改成员的分数(scrore)并更新到正确的排序位置。
如果key不存在,将会创建一个新的有序集合(sorted set)并将分数/成员(score/member)对添加到有序集合,就像原来存在一个空的有序集合一样。如果key存在,但是类型不是有序集合,将会返回一个错误应答
如果key不存在,将会创建一个新的有序集合(sorted set)并将分数/成员(score/member)对添加到有序集合,就像原来存在一个空的有序集合一样。如果key存在,但是类型不是有序集合,将会返回一个错误应答
参数
- XX: 仅仅更新存在的成员,不添加新成员。
- NX: 不更新存在的成员。只添加新成员。
- CH: 修改返回值为发生变化的成员总数,原始是返回新添加成员的总数 (CH 是 changed 的意思)。更改的元素是新添加的成员,已经存在的成员更新分数。 所以在命令中指定的成员有相同的分数将不被计算在内。注:在通常情况下,
ZADD
返回值只计算新添加成员的数量。 - INCR: 当
ZADD
指定这个选项时,成员的操作就等同ZINCRBY命令,对成员的分数进行递增操作
ZCARD
返回key的有序集元素个数
ZREM
返回的是从有序集合中删除的成员个数,不包括不存在的成员
数据结构原理
SDS(简单动态字符串)
具有动态增加空间的能力,可以自动扩容
len
SDS 所保存的字符串长度。这样获取字符串长度的时候,只需要返回这个变量值就行,时间复杂度只需要 O(1)
alloc
分配给字符数组的空间长度。这样在修改字符串的时候,可以通过 alloc - len 计算 出剩余的空间大小,然后用来判断空间是否满足修改需求,如果不满足的话,就会自动将 SDS 的空间扩展至执行修改所需的大小,然后才执行实际的修改操作,所以使用 SDS 既不需要手动修改 SDS 的空间大小,也不会出现前面所说的缓冲区益处的问题
flags
SDS 类型,用来表示不同类型的 SDS。一共设计了 5 种类型,分别是 sdshdr5、sdshdr8、sdshdr16、sdshdr32 和 sdshdr64,后面在说明区别之处
buf[]
字节数组,用来保存实际数据。不需要用 “\0” 字符来标识字符串结尾了,而是直接将其作为二进制数据处理,可以用来保存图片等二进制数据。它即可以保存文本数据,也可以保存二进制数据,所以叫字节数组会更好点
INT SET
set数据类型的底层实现数据结构
DICT
hash的底层数据结构,也是RedisBb用来存储K-V的数据结构,当数据量比较小,或者单个元素比较小时,底层用ziplist存储,数据大小和元素数量阈值可以通过如下参数设置
hash-max-ziplist-entries 512 // ziplist 元素个数超过 512 ,将改为hashtable编码
hash-max-ziplist-value 64 // 单个元素大小超过 64 byte时,将改为hashtable编码
ZIP LIST
是由一系列特殊编码的连续内存块组成的顺序存储结构,类似于数组,ziplist在内存中是连续存储的,但是不同于数组,为了节省内存 ziplist的每个元素所占的内存大小可以不同(数组中叫元素,ziplist叫节点entry),每个节点可以用来存储一个整数或者一个字符串
是List、Zset、Hash数据类型的底层数据结构之一
zlbytes
ziplist的长度(单位: 字节),是一个32位无符号整数,记录整个压缩列表占用对内存字节数
zltail
记录压缩列表「尾部」节点距离起始地址由多少字节,也就是列表尾的偏移量;,反向遍历ziplist或者pop尾部节点的时候有用
zllen
ziplist的节点(entry)个数 entry: 节点 zlend: 值为0xFF,用于标记ziplist的结尾
QUICK LIST
在Redis3.2.0版本的时候引入了quicklist链表结构,结合了linkedlist和ziplist的优势
SKIP LIST
是Zset的底层数据结构,有着高性能的查找排序能力
LINKED LIST
Redis List数据类型底层就是基于链表来实现
REDIS OBJECT
Redis内部每一种数据类型都是对象化的,也就是我们所说的5种数据类型其实内部都会对应到Redis Object对象,然后再由Redis Object来包装具体的存储数据结构和编码
内存管理策略
内存模型
对象内存
对象内存,是Redis内存占用最大的一块,存储着用户所有的数据。Redis所有的数据都采用key-value格式,每次创建键值对时,至少创建两个类型对象:key对象和value对象。key对象都是字符串,value对象包含五种数据结构类型。所以,对象内存消耗可以简单理解为sizeof(keys)+sizeof(values)
缓存内存
客户端缓冲区
指的是所有接入到Redis服务器TCP连接的输入输出缓冲
复制积压缓冲区
edis在2.8版本之后提供了一个可重用的固定大小缓冲区,用于实现部分复制功能,根据repl-backlog-size参数控制,默认1MB。对于复制积压缓冲区,只有Master节点存在,所有Slave节点共享此缓冲区
AOF缓冲区
用于在Redis重写期间保存最近的写入命令,其消耗的内存取决于AOF重写时间和写入量,这部分空间占用通常很小
子进程内存消耗
主要是在执行AOF/RDB重写时,Redis创建的子进程内存消耗。Redis执行fork操作产生的子进程,内存占用量与父进程相同,理论上需要一倍的物理内存来完成重写操作
info memory(内存数据统计)
used_memory
Redis分配器分配的内存总量,也就是内部存储的所有数据内存占用量
user_momery_rss
从操作系统的角度显示Redis进程占用的物理内存总量
mem_fragmentation_ratio
内存碎片率,used_memory_rss/used_memory的比值
mem_fragmentation_ratio>1时,说明多出的部分内存并没有用于数据存储,而是被内存碎片所消耗;
mem_fragmentation_ratio<1时 ,说明可能是操作系统把Redis内存swap到磁盘导致。
mem_fragmentation_ratio<1时 ,说明可能是操作系统把Redis内存swap到磁盘导致。
内存关联
回收策略
定期删除
指的是Redis默认每隔100ms就随机抽取一些设置了过期时间的key,检查其是否过期,如果过期就删除
惰性删除
就是指当客户端获取某个key的时候,Redis会检查这个key是否设置了过期时间,如果设置了是否已过期?如果过期了就会删除,不会给客户端返回任何东西。
淘汰策略
定期删除和惰性删除只能解决内存空间足够情况下的内存回收,如果大量过期key堆积在内存里,导致Redis内存耗尽,或者说内存使用达到了maxmemory上限,此时则会对内存进行淘汰
noeviction
默认策略,不会删除任何数据,拒绝所有写入操作并返回客户端错误信息;(一般没人用)
volatile-lru
根据LRU算法,删除设置了超时属性(expire)的键,直到腾出足够空间为止。如果没有可删除的键对象,回退到noeviction策略;(一般很少使用)
allkeys-lru
根据LRU算法删除key,不管数据有没有设置超时属性,直到腾出足够空间为止;(最常用)
allkeys-random
随机删除所有键,直到腾出足够空间为止;(一般没人用)
volatile-random
随机删除过期键,直到腾出足够空间为止
volatile-ttl
在设置了过期时间的键空间中,有更早过期时间的key优先移除。如果没有,回退到noeviction策略
数据持久化
RDB持久化
描述
是一次性生成内存快照的方式,也就是把当前Redis进程的数据生成快照,保存到磁盘上。触发RDB持久化的方式有手动触发和自动触发,生产环境一般都是自动触发,都是通过bgsave命令
原理
执行bgsave命令后,Redis主进程会执行fork操作创建子进程,RDB持久化过程由子进程负责,完成后自动结束。阻塞只发生在fork阶段,一般时间很短
流程
1,执行bgsave命令,Redis父进程判断当前是否存在正在执行的子进程,如RDB/AOF子进程,如果存在则直接返回;
2,父进程执行fork操作创建子进程,fork操作过程中父进程会阻塞;
3,父进程fork完成后,bgsave命令返回“Background saving started”信息,并不再阻塞父进程,父进程可以继续响应其他命令;
4,子进程创建RDB文件,根据父进程内存生成临时快照文件,完成后对原有文件进行原子替换;
5,子进程发送信号通知父进程RDB持久化完成,父进程更新统计信息
2,父进程执行fork操作创建子进程,fork操作过程中父进程会阻塞;
3,父进程fork完成后,bgsave命令返回“Background saving started”信息,并不再阻塞父进程,父进程可以继续响应其他命令;
4,子进程创建RDB文件,根据父进程内存生成临时快照文件,完成后对原有文件进行原子替换;
5,子进程发送信号通知父进程RDB持久化完成,父进程更新统计信息
优缺点
优点
1,RDB会生成多个数据文件,每个数据文件都代表了某一个时刻中Redis的数据,这种多个数据文件的方式,非常适合做定期的冷备;
2,RDB对Redis正常的读写服务影响非常小,可以让Redis保持高性能,因为Redis主进程只需要fork一个子进程,让子进程执行磁盘IO操作来进行RDB持久化即可;
3,相对于AOF持久化机制来说,直接基于RDB数据文件来重启和恢复数据更加快速
2,RDB对Redis正常的读写服务影响非常小,可以让Redis保持高性能,因为Redis主进程只需要fork一个子进程,让子进程执行磁盘IO操作来进行RDB持久化即可;
3,相对于AOF持久化机制来说,直接基于RDB数据文件来重启和恢复数据更加快速
缺点
1,RDB持久化不能做到实时,因为频繁fork子进程的开销是很大的。所以如果想要在Redis故障时,尽可能减少数据丢失,那应该开启AOF。一般来说,RDB数据快照文件,都是每隔5分钟或者更长时间生成一次;
2,RDB每次fork子进程生成数据快照文件时,如果数据文件特别大,可能会导致对客户端提供的服务暂停数毫秒,甚至数秒
2,RDB每次fork子进程生成数据快照文件时,如果数据文件特别大,可能会导致对客户端提供的服务暂停数毫秒,甚至数秒
AOF持久化
描述
是对每条写入命令以append-only的模式写入一个AOF日志文件中,在Redis重启的时候,可以通过回放AOF日志中的写入指令来重新构建整个数据集
原理
通过appendonly 开启AOF持久化
流程
1,命令追加
Redis的所有写入命令(以文本协议格式)会追加到aof_buf缓冲区的末尾,这个缓冲区其实就是操系统的filesystem cache,可以大幅提升磁盘IO的性能
2,文件写入
写入策略(参数appendfsync控制)
always
每次写入AOF缓存后,立即调用操作系统的fsync命令,将数据刷到磁盘。一般不建议配置,因为会大幅降低吞吐量,除非对数据可用性有非常高的要求
no
由操作系统自身去控制何时将os cache中的数据刷到磁盘上,同步周期不可控。一般也不建议配置
everysec
每隔1秒钟(默认),调用操作系统的fsync命令将数据刷到磁盘。推荐配置
3,文件重写
随着AOF日志文件越来越大,需要定期对AOF文件进行rewrite,以达到压缩的目的。
因为AOF会记录每一条写命令,所以会导致对于同一个Key,存在多个冗余命令。Redis会创建一个新的AOF文件来替换老文件,新旧两个文件所保存的数据状态完全相同,但是新文件不会包含任何浪费空间的冗余命令
因为AOF会记录每一条写命令,所以会导致对于同一个Key,存在多个冗余命令。Redis会创建一个新的AOF文件来替换老文件,新旧两个文件所保存的数据状态完全相同,但是新文件不会包含任何浪费空间的冗余命令
重写流程(主进程通过bgrewriteaof命令进行重写)
1,父进程fork一个子进程;,
2,子进程根据当前的内存快照,按照命令合并规则(避免命令冗余),写入到新的AOF文件;
3,在此期间,主进程会把接收到的新的写入命令追加到AOF缓存区(aof_buf),同时再写入一份到AOF重写缓存区(aof_rewrite_buf);
4,子进程完成新的AOF文件写入后,会发送信号给父进程;
5,父进程接受到信号后,把aof_rewrite_buf的数据写入到新的AOF文件中,这样新AOF文件中的数据状态就和当前数据状态是一致的了;
6,重命名新的AOF文件,覆盖掉老文件,完成AOF重写
2,子进程根据当前的内存快照,按照命令合并规则(避免命令冗余),写入到新的AOF文件;
3,在此期间,主进程会把接收到的新的写入命令追加到AOF缓存区(aof_buf),同时再写入一份到AOF重写缓存区(aof_rewrite_buf);
4,子进程完成新的AOF文件写入后,会发送信号给父进程;
5,父进程接受到信号后,把aof_rewrite_buf的数据写入到新的AOF文件中,这样新AOF文件中的数据状态就和当前数据状态是一致的了;
6,重命名新的AOF文件,覆盖掉老文件,完成AOF重写
优缺点
优点
1,AOF可以更好的保护数据不丢失,AOF默认每隔1秒,通过一个后台线程执行一次fsync操作,所以最多丢失1秒钟的数据;
2,AOF日志文件以append-only模式写入,所以没有任何磁盘寻址的开销,写入性能非常高,而且文件不容易损坏,即使文件尾部破损,也很容易修复;
3,AOF日志文件过大的时候,即使出现重写,由于是后台操作,所以也不会影响客户端的读写。
4,AOF日志文件的命令以可读的方式进行记录,非常适合做灾难性的误删除操作的紧急恢复。比如某人不小心用flushall命令清空了所有数据,只要这个时候后台rewrite还没有发生,那么就可以立即拷贝AOF文件,将最后一条flushall命令删除,然后再将该AOF文件放回去,就可以通过恢复机制,自动恢复所有数据。
2,AOF日志文件以append-only模式写入,所以没有任何磁盘寻址的开销,写入性能非常高,而且文件不容易损坏,即使文件尾部破损,也很容易修复;
3,AOF日志文件过大的时候,即使出现重写,由于是后台操作,所以也不会影响客户端的读写。
4,AOF日志文件的命令以可读的方式进行记录,非常适合做灾难性的误删除操作的紧急恢复。比如某人不小心用flushall命令清空了所有数据,只要这个时候后台rewrite还没有发生,那么就可以立即拷贝AOF文件,将最后一条flushall命令删除,然后再将该AOF文件放回去,就可以通过恢复机制,自动恢复所有数据。
缺点
1,对于同一份数据来说,AOF日志文件通常比RDB数据快照文件更大;
2,AOF开启后,支持的写QPS会比RDB低,因为AOF一般会配置成每秒fsync一次日志文件;
3,AOF这种基于命令日志回放的方式,比RDB每次持久化一份完整数据快照的方式,更加脆弱一些,容易有bug。不过AOF就是为了避免rewrite过程导致的bug,因此每次rewrite并不是基于旧的指令日志进行merge的,而是基于当时内存中的数据,进行指令的重新构建,这样健壮性会好很多
2,AOF开启后,支持的写QPS会比RDB低,因为AOF一般会配置成每秒fsync一次日志文件;
3,AOF这种基于命令日志回放的方式,比RDB每次持久化一份完整数据快照的方式,更加脆弱一些,容易有bug。不过AOF就是为了避免rewrite过程导致的bug,因此每次rewrite并不是基于旧的指令日志进行merge的,而是基于当时内存中的数据,进行指令的重新构建,这样健壮性会好很多
redis高可用
redis复制
全量复制
部分复制
redis哨兵模式
redis集群模式
0 条评论
下一页