参考:《Redis开发与运维》
Redis为什么速度快
- 所有数据都在内存中
- C语言实现
- 单线程架构避免多线程可能产生的竞争问题。(注意事项:如果某个命令执行过长会阻塞其他命令,所以Redis是面向快速执行的场景的数据库)
- IO多路复用
- 源码写的好
API的理解和使用
-
常用全局命令
-
keys
查看所有key 会遍历所有键,线上环境应禁止使用,时间复杂度O(n) -
dbsize
键总数:不会遍历所有key,时间复杂度O(1) -
expire key seconds
过期后会自动删除。( 注意事项:过期是以对象为单位,比如一个 hash 结构的过期是整个 hash 对象的过期,而不是其中的某个子 key) -
type key
查看键类型 -
object encoding
查询内部编码。( 注意事项:每种数据结构都有自己的底层内部编码而且多种实现)
客户端命令执行流程: 客户端 –> [ 命令队列 ] –> 执行 –> 返回结果
键管理注意事项:
- 如果键过期时间为负数则立即被删除
- persist 删除键过期时间
- 对于字符串那类型的键,Set 命令会去掉过期时间
- redis默认配置了16个库。Select dbIndex切换库。3.0之后Redis Cluster只允许使用0号数据库
- flushdb/flushall 会清空所有数据。如果key较多则存在阻塞redis的可能
字符串
简介:
- 最基础的数据结构,所有的键都是字符串同是其他几种数据结构都是在字符串的基础上构建的.
- 字符串类型可以是简单 字符串、xml、json、整数、二进制(图片、音视频)。
- 最大长度是512M
- 每个中文占三个字节
- Redis 的字符串是动态字符串,是可以修改的字符串,内部结构实现上类似于 Java 的 ArrayList,采用预分配冗余空间的方式来减少内存的频繁分配。内部为当前字符串实际分配的空间 capacity 一般要高于实际字符串长度 len。当字符串长度小于 1M 时,扩容都是加倍现有的空间,如果超过 1M,扩容时一次只会多扩 1M 的空间
常用命令
命令 | 说明 |
---|---|
ex | 秒级过期时间 |
px | 毫秒级过期时间 |
nx | 不存在才能设置成功。set nx 可以作为分布式锁的一种实现方案 http://redis.io/topics/distlock |
xx | 与nx相反 |
incr | 自增 |
incrby | 自定义自增步长 |
decr | 自减 |
decrby | 自定义自减步长 |
内部编码
编码类型 | 说明 |
---|---|
int | 8字节长整型 |
enmstr | <=39字节的字符串 |
raw | 大于39字节的字符串 |
典型使用场景
- 缓存
- 计数
- 共享session
- 限速
哈希
简介:
- Redis 的字典相当于 Java 语言里面的 HashMap,它是无序字典。不同的是,Redis 的字典的值只能是字符串。另外它们 rehash 的方式不一样,因为 Java 的 HashMap 在字典很大时,rehash 是个耗时的操作,需要一次性全部 rehash。Redis 为了高性能,不能堵塞服务,所以采用了渐进式 rehash 策略
- 渐进式 rehash 会在 rehash 的同时,保留新旧两个 hash 结构,查询时会同时查询两个 hash 结构,然后在后续的定时任务中以及 hash 操作指令中,循序渐进地将旧 hash 的内容一点点迁移到新的 hash 结构中。当搬迁完成了,就会使用新的hash结构取而代之
注意:
hgetall
如果hash元素个数比较多的话可能导致redis阻塞。可以是使用hscan
命令,该命令是渐进式遍历hash类型。参看scan1
内部编码:
编码类型 | 说明 |
---|---|
ziplist 2 | 单hash元素个数小于阈值512个(默认)同时每个value都小于64字节(默认)时使用ziplist存储。这里的两个默认值都是可以设置的 |
hashtable | 不满足ziplist时使用 |
列表
简介:
- Redis 的列表相当于 Java 语言里面的 LinkedList,注意它是链表而不是数组。这意味着 list 的插入和删除操作非常快,时间复杂度为 O(1),但是索引定位很慢,时间复杂度为 O(n)
- 一个列表最多可以 232-1 个元素
- index 相当于 Java 链表的get(int index)方法,它需要对链表进行遍历,性能随着参数index增大而变差
常见命令组合成数据结构:
- lpush + lpop = 栈
- lpush + rpop = 队列
- lpush + ltrim = 有限集合
- lpush + brpop = 消息队列
内部编码:
编码类型 | 说明 |
---|---|
ziplist 3 | 当元素个数小于阈值512个(默认)同时每个value都小于64字节(默认)时使用ziplist存储。这里的两个默认值都是可以设置的 |
quicklist | Redis3.2 版本提供了quicklist内部编码,将linkedlist和 ziplist 结合起来组成了 quicklist。也就是将多个 ziplist 使用双向指针串起来使用。这样既满足了快速的插入删除性能,又不会出现太大的空间冗余 |
集合Set
简介:
- redis 的集合相当于 Java 语言里面的 HashSet,它内部的键值对是无序的唯一的。它的内部实现相当于一个特殊的字典,字典中所有的 value 都是一个值NULL。
- 集合计算:交集、并集、差集。当元素比较多时会比较耗时。
- 集合命令+store = 将计算后的结果保存到新的集合中
内部编码:
编码类型 | 说明 |
---|---|
intset | 当集合内都是整数并且元素个数小于等512个(默认的)时使用 |
hashtable | 不满足intset时使用 |
应用场景:
sadd
= 标签spop/srandmember
= Ramdom item 生成随机数:比如抽奖sadd + sinter
= 社交需求,计算用户共同感兴趣的标签
有序集合
简介:
zset类似于 Java 的 SortedSet 和 HashMap 的结合体,一方面它是一个 set,保证了内部 value 的唯一性,另一方面它可以给每个 value 赋予一个 score,代表这个 value 的排序权重。
注意:
由于需要排序zadd
时间复杂度为O(log(n)), sadd
时间复杂度为O(1)
常用命令
命令 | 说明 |
---|---|
zadd nx | 不存在才能设置成功 用于添加 |
zadd xx | 存在才能设置成功 用于修改 |
zadd ch | 返回操作后元素变化的个数 |
zadd incr | 增加分数 = zincrby 命令 |
内部编码:
编码类型 | 说明 |
---|---|
ziplist 4 | 当元素个数小于128个(默认)同时每个value都小于64字节(默认)时使用ziplist存储。这里的两个默认值都是可以设置的。 |
skiplist5 | 不满足ziplist时使用。 |
应用场景:
zadd
zincrby
= 用户点赞zrevrange
= 最多的用户点赞数- 排行榜
- scan
* 渐进式遍历. Redis的存储是hashtable数据结构,scan通过cursor方式每次只扫描一部分key。
* 存在的问题:在scan过程中键发生变(增删改)则无法保证完整的遍历出所有的键 ↩ - ziplist
* ziplist是一个经过特殊编码的双向链表,它的设计目标就是为了提高存储效率。ziplist可以用于存储字符串或整数,其中整数是按真正的二进制表示进行编码的,而不是编码成字符串序列。
* ziplist采用了一段连续的内存来存储数据,相比hashtable减少了内存碎片,和指针的内存占用。而且当节点较少时,ziplist更容易被加载到CPU缓存中.
* 它能以O(1)的时间复杂度在表的两端提供push和pop操作,这种结构并不擅长做修改操作 ↩ - ziplist
* ziplist是一个经过特殊编码的双向链表,它的设计目标就是为了提高存储效率。ziplist可以用于存储字符串或整数,其中整数是按真正的二进制表示进行编码的,而不是编码成字符串序列。
* ziplist采用了一段连续的内存来存储数据,相比hashtable减少了内存碎片,和指针的内存占用。而且当节点较少时,ziplist更容易被加载到CPU缓存中.
* 它能以O(1)的时间复杂度在表的两端提供push和pop操作,这种结构并不擅长做修改操作 ↩ - ziplist
* ziplist是一个经过特殊编码的双向链表,它的设计目标就是为了提高存储效率。ziplist可以用于存储字符串或整数,其中整数是按真正的二进制表示进行编码的,而不是编码成字符串序列。
* ziplist采用了一段连续的内存来存储数据,相比hashtable减少了内存碎片,和指针的内存占用。而且当节点较少时,ziplist更容易被加载到CPU缓存中.
* 它能以O(1)的时间复杂度在表的两端提供push和pop操作,这种结构并不擅长做修改操作 ↩ - skiplist
* 因为 zset 要支持随机的插入和删除,所以它不好使用数组来表示
* 当有新元素需要插入时,要定位到特定位置的插入点,这样才可以继续保证链表是有序的
* 跳跃表就是类似”亚洲–>中国->安徽省->安庆市->枞阳县->汤沟镇->田间村->xxxx号”这样一个类似的结构,最下面一层所有的元素都会串起来。然后每隔几个元素挑选出一个代表来,再将这几个代表使用另外一级指针串起来。然后在这些代表里再挑出二级代表,再串起来。最终就形成了金字塔结构。 ↩