1 服务器中的数据库
Redis服务器将所有数据库都保存在服务器状态redis.h/redisServer
结构的db数组
中,db数组的每个项都是一个redis.h/redisDb
结构,每个redisDb结构代表了一个数据库。
1 | struct redisServer { |
dbnum属性的值由服务器配置的database选项决定,默认情况下,该选项的值为16。
2 切换数据库
默认情况下,Redis客户端的目标数据库为0号数据库
,通过select命令
可切换数据库。
示例:
1 | # 切换到数据库2. |
其中redisClient
结构中的db属性记录了当前客户端的目标数据库
,该属性是一个指向redisDb
结构的指针,redisClient.db
指向redisServer.db
数组的其中一个元素,被指向的元素即为目标数据库
。
1 | typedef struct redisClient { |
下图为客户端的目标数据库为1号数据库。
问: SELECT命令的实现原理是什么?
通过修改redisClient.db指针,让它指向服务器中不同数据库,从而实现切换目标数据库的功能。
3 数据库键空间
Redis是一个键值对数据库服务器,服务器中的每个数据库都由一个redis.h/redisDb
结构表示,其中,redisDb
结构的dict
字典保存了数据库中的所有键值对,我们将这个字典称为键空间(key space)。
redis.h/redisDb
结构:
1 | typedef struct redisDb { |
其中,
- 键空间的键就是数据库的键,每个键都是一个
字符串对象
; - 键空间的值就是数据库的值,每个值都可以是
字符串对象、列表对象、哈希表对象、集合对象和有序集合对象中的任意一种Redis对象
。
示例:下图为包含了列表键、哈希表键、字符串键的结构图
注:数据库的键空间是一个字典,所以所有针对数据库的操作,实际上都是通过对键空间字典进行操作来实现的。
- 添加新键: 添加新键值对到数据库,实际上就是将一个新键值对添加到键空间字典里面,其中键为字符串对象,而值则为任意一种类型的Redis对象。
- 删除键: 删除数据库中的一个键,实际上就是在键空间里面删除键所对应额键值对对象。(键对象和值对象都要删除!)
- 更新键: 更新数据库键,实际上是对键空间里面键所对应的值对象进行更新,根据值对象的类型不同,更新的具体方法也会有所不同。
- 对键取值: 取值,实际上就是在键空间中取出键所对应的值对象,根据值对象的类型不同,具体的取值方法也会有所不同。
3.1 其他键空间的操作
除了以上增删改查的操作,还有其他一些命令是通过对键空间进行处理来完成的。
比如说:
flushdb
命令: 通过删除键空间中的所有键值对来实现的。randomkey
命令: 随机返回某个键,也是在键空间中随机返回的。dbsize
命令: 返回数据库键数量,通过返回键空间中包含的键值对的数量来完成。exist、rename、keys
等。
3.2 读写键空间时的维护操作
当时用redis命令对数据库进行读写时,服务器不仅会对键空间执行指定的读写操纵,还会执行一些额外的维护操作,比如:
①、在读取一个键之后(读写操作都会对键进行读取),服务器会根据键是否存在来更新服务器的键空间命中(keyspace_hit)/未命中(keyspace_misses)的次数。通过以下命令可查看其属性。
1
INFO stats
②、在读取一个键之后,服务器会更新键的LRU(最后一次使用)时间,这个值可以用于计算键的闲置时间,使用以下命令可查看键的闲置时间。
1
object idletime <key>
③、若服务器在读取一个键时发现该键已过期,那么服务器会先删除这个过期键,然后执行余下的其他操作。
④、若有客户端使用watch命令监视了这个键,那么服务器在对被监视的键进行修改之后,会标记这个键为脏(dirty),从而让事务成武注意到这个键已经被修改过。
⑤、服务器每次修改一个键之后,都会对dity键计数器的值增1,这个计数器会触发服务器的持久化以及复制操作。
⑥、若服务器开启了数据库通知功能,那么在对键进行修改之后,服务器将按配置发送相应的数据通知。
4 数据库通知
数据库通知可以让客户端通过订阅给定频道或者模式,来获悉数据库中键的变化,以及数据中命令的执行情况。