文章目录
- 一. 核心命令
- 1. set
- 2. get
- 二. 全局命令
- 1. keys
- 2. exists
- 3. del
- 4. expire
- 5. ttl
- 6. type
- 三. redis 常见的数据结构及内部编码
- 四. redis单线程模型
一. 核心命令
1. set
set key value
key 和 value 都是string类型的
对于key value, 不需要加上引号, 就是表示字符串类型, 加上也可以
redis中, 不区分大小写
2. get
get key
如果key不存在, 返回nil, 就是null的意思
二. 全局命令
全局命令, 就是能够搭配任意一个数据结构来使用的命令
1. keys
keys pattern
pattern就是通过一些特殊符号(通配符), 来描述key的模样, 匹配上的key就能被查询出来
keys的时间复杂度是O(N)
在生产环境上, 一般会禁止使用keys命令, 尤其是keys *
- 办公环境
- 开发环境 后端程序开发的环境, 硬件配置高
- 测试环境 测试工程师使用的
4.线上环境/生产环境 外界用户可以访问到的
2. exists
判断key是否存在, 返回key存在的个数(针对多个key是有用的)
时间复杂度: O(1)
redis组织这些key就是按照哈希表的方式来组织的
redis自身的这些键值对, 是通过哈希表的方式来组织的, redis具体的某个值, 又可以是多种数据结构
3. del
删除指定的key, 可以一次删除一个或多个
返回删除key的个数(针对多个key是有用的)
时间复杂度: O(1)
如果redis作为缓存, redis中存的是热点数据, 全量数据是在mysql中存着, 那么redis使用del, 删除几个key, 一般来说问题不大, 相比之下, 如果是mysql数据, 哪怕误删了一个数据, 都可能是影响很大的
如果redis作为数据库, 此时误删数据影响就大了
4. expire
给指定的key设置过期时间, 单位是秒, key存活超出这个指定的值, 就会被自动删除
必须是针对已经存在的key设置过期时间
(pexpire key 毫秒)
返回0设置失败, 返回1设置成功
时间复杂度: O(1)
5. ttl
time to live 查看当前key的过期时间还剩多少
(pttl key )
返回值: 剩余过期时间。-1 表示没有关联过期时间,-2 表示 key 不存在
时间复杂度: O(1)
redis 的 key 的过期策略是怎么实现的?
redis的整体策略是: 定期删除 和 惰性删除 相结合
惰性删除: 假设key已经过期, 但暂时还没有删除, 后面如果有一次访问, 正好用好这个key, 于是这个访问就会让redis服务器触发删除key的操作, 同时再返回一个nil
定期删除: 每次抽取一部分, 进行验证过期时间, 保证验证的过程足够快
(因为redis是单线程的程序, 如果扫描过期key消耗的时间太多了, 就可能导致正常处理请求命令就阻塞了)
上述过期策略, 任然可能有很多过期的key被残留, redis又提供了一系列的内存淘汰策略
6. type
返回key对应value的数据类型
三. redis 常见的数据结构及内部编码
redis在底层实现这些数据结构的时候, 会在源码层面, 针对上述实现进行特定的优化(编码方式可能有变数), 达到节省时间/空间 的效果
同一个数据类型, 背后可能编码方式是不同的, 会根据具体场景优化
redis自动适应, 程序猿使用时是感觉不到的
- raw
最基本的字符串, 底层就是一个byte数组在存放数据 - int
当value是个整数的时候, 此时可能redis会直接使用int来存储数据
redis通常也可以用来实现"计数"这样的功能 - embstr
针对短字符串进行的特殊优化 - hashtable
最基本的哈希表 - ziplist
压缩列表, 在哈希表中元素比较少的时候, 并且value的值长度比较短, 可能会优化成ziplist
压缩: 压缩的本质, 是针对数据进行重新编码, 不同的数据有不同的特点, 结合这些特点进行精妙的设计, 重新编码后, 就能够缩小体积, 但付出的代价就是读写速度慢
- linkelist
链表 - ziplist
压缩列表, 在列表中元素比较少的时候, 可能会优化成ziplist
(从redis3.2开始, 引入了新的quicklist, 代替上面两个) - hashtable
最基本的哈希表 - inset
集合里都是整数 - skiplist
跳表, 也是链表, 不同于普通的链表, 每个节点上有多个指针域, 巧妙地搭配这些指针域的指向, 就可以做到, 从跳表上查询元素的时间复杂度是O(logN) - ziplist
压缩列表, 在列表中元素比较少的时候, 可能会优化成ziplist
object encoding mylist
可以查看底层的编码方式
四. redis单线程模型
我们知道, 如果多个客户端, 同时对服务器发起请求, 可能会引起线程不安全问题
但是, redis服务器不会发生
redis服务器实际上是单线程模型, 保证了当前收到的多个请求是串行执行的
多个请求同时到达redis服务器, 也是要先在队列中排队, 等待redis服务器一个一个取出里面的命令再执行
微观上讲, redis服务器是串行/顺序执行这么多命令的
总结: redis只使用一个线程, 来处理所有的命令请求, 但不是说redis服务器进程内部真的只有一个线程, 其他也有线程, 多个线程是在处理网络IO
redis之所以能够使用单线程工作, 是因为redis的核心业务逻辑, 都是短平快的, 不太消耗cpu资源, 单线程可以处理
但单线程带来的弊端就是, 必须特别小心, 某个操作占用时间长, 阻塞其他命令执行
redis虽然是单线程模型, 为啥效率那么高, 速度那么快?
- redis访问内存, 数据库访问硬盘
- redis的核心业务, 比数据库的核心功能更简单(redis干的活少, 提供的功能相比mysql也是少了不少)
- 单线程模型, 避免了一些不必要的线程竞争开销
- 处理网络IO, 使用了epoll这样的IO多路复用机制
IO多路复用:
概念: 一个线程, 管理多个socket
针对TCP来说, 服务器每次要服务一个客户端, 都需要给客户端安排一个socket
一个服务器多个客户端, 同时就有多个socket
很多情况下, 每个客户端和服务器之间的通信没有那么频繁, 此时, 这么多socket大部分时间是静默的, 上面是没有数据需要传输的
所以, 同一时刻, 只有少数socket活跃, 所以引入IO多路复用, 一个线程处理多个socketIO多路复用, 是操作系统给程序猿提供的机制, 提供了一套API, 内部的功能都是操作系统内核实现的
Linux上提供了IO多路复用, 主要有三套: select, poll, epoll
epoll 时间通知/回调机制