前言
大家好,牧码心今天给大家推荐一篇redis系列之内部结构的文章,在实际工作中有很多应用场景,希望对你有所帮助。内容如下:
- 全局命令
- 内部编码
- 单线程架构
全局命令
Redis有5种数据结构, 它们是键值对中的值, 对于键来说有一些通用的命令
- 查询所有键值
命令:keys *
127.0.0.1:6379> keys *
1) "python"
2) "java"
3) "hello"
- 键总数
命令:dbsize
127.0.0.1:6379> dbsize
(integer) 4
说明:dbsize命令在计算键总数时不会遍历所有键, 而是直接获取Redis内置的键总数变量, 所以dbsize命令的时间复杂度是O(1) 。 而keys命令会遍历所有键, 所以它的时间复杂度是O(n) , 当Redis保存了大量键时, 线上环境禁止使用。
-
检查键是否存在
命令:exists key
说明:如果键存在则返回1, 不存在则返回0 -
删除键
命令:del key [key …]
说明:返回结果为成功删除键的个数, 假设删除一个不存在的键, 就会返回0 -
键过期
命令:expire key seconds
说明:命令会返回键的剩余过期时间, 它有3种返回值:- 大于等于0的整数: 键剩余的过期时间。
- -1: 键没设置过期时间。
- -2: 键不存在
#还剩7秒
127.0.0.1:6379> ttl hello
(integer) 7
...
#还剩1秒
127.0.0.1:6379> ttl hello
(integer) 1
#返回结果为-2, 说明键hello已经被删除
127.0.0.1:6379> ttl hello
(integer) -2
127.0.0.1:6379> get hello
(nil)
- 查看键的数据类型
命令:type key
127.0.0.1:6379> set a b
OK
127.0.0.1:6379> type a
string
127.0.0.1:6379> rpush mylist a b c d e f g
(integer) 7
127.0.0.1:6379> type mylist
list
说明:键hello是字符串类型, 返回结果为string。 键mylist是列表类型, 返回结果为list
内部编码
redis对外支持的数据结构分别是:string(字符串)/hash(哈希)/ list(列表) /set(集合)/zset(有序集合),但实际上每种数据结构都有自己底层的内部编码实现, 而且是多种实现,这样Redis会在合适的场景选择合适的内部编码。
-
结构和编码图
-
设计优势
1、可以改进内部编码, 而对外的数据结构和命令没有影响, 这样一旦开发出更优秀的内部编码, 无需改动外部数据结构和命令。
例如Redis3.2提供了quicklist, 结合了ziplist和linkedlist两者的优势, 为列表类型提供了一种更为优秀的内部编码实现, 而对外部用户来说基本感知不到;
2、多种内部编码实现可以在不同场景下发挥各自的优
势, 例如ziplist比较节省内存, 但是在列表元素比较多的情况下, 性能会有所下降, 这时候Redis会根据配置选项将列表类型的内部实现转换为linkedlist;
单线程结构
- 实例说明
开启了三个redis-cli客户端同时执行命令
1、客户端1设置一个字符串键值对:
127.0.0.1:6379> set hello world
2、客户端2对counter做自增操作:
127.0.0.1:6379> incr counter
3、客户端3对counter做自增操作:
127.0.0.1:6379> incr counter
Redis客户端与服务端的模型可以简化成图, 每次客户端调用都经历了发送命令、 执行命令、 返回结果三个过程:
上面3个客户端命令的执行顺序是不确定的,但是可以确定不会有两条命令被同时执行,所以两条incr命令无论怎么执行最终结果都是2, 不会产生并发问题, 这就是Redis单线程的基本模型,如图所示:
-
单线程快的原因
1、纯内存访问, Redis将所有数据放在内存中, 内存的响应时长大约为100纳秒, 这是Redis达到每秒万级别访问的重要基础;
2、非阻塞I/O, Redis使用epoll作为I/O多路复用技术的实现, 再加上Redis自身的事件处理模型将epoll中的连接、 读写、 关闭都转换为事件, 不在网络I/O上浪费过多的时间;
3、单线程避免了线程切换和竞态产生的消耗; -
存在问题
如果某个命令执行过长, 会造成其他命令的阻塞, 对于Redis这种高性能的服务来说是致命的, 所以Redis是面向快速执行场景的数据库
参考
- 《redis运维与开发》