Redis实现之数据库(一)

摘要:
默认情况下,Redis客户端的目标数据库为0号数据库,但客户端可以通过执行SELECT命令来切换目标数据库。}redisClient;redisClient.db指针指向redisServer.db数组的其中一个元素,而被指向的元素就是客户端你的目标数据库。

服务器中的数据库

Redis服务器将所有数据库都保存在服务器状态redis.h/redisServer结构体的db数组中,db数组的每个项都是一个redis.h/redisDb结构体,每个redisDb结构体代表一个数据库

redis.h

struct redisServer {
    ……
	//一个数组,保存着服务器中所有数据库
    redisDb *db;
    ……
};

在初始化服务器时,程序会根据服务器状态的dbnum属性来决定应该创建多少个数据库:

redis.h

struct redisServer {
    ……
	//服务器的数据库数量
    int dbnum;                     
    ……
};

dbnum属性由服务器配置的database选项决定,默认情况下,该选项的值为16,所以Redis服务器默认会创建16个数据库,如图1-1所示

Redis实现之数据库(一)第1张

图1-1 服务器数据库示例

切换数据库

每个Redis客户端都有自己的目标数据库,每当客户端执行数据库写命令或者数据库读命令的时候,目标数据库就会成为这些命令的操作对象。默认情况下,Redis客户端的目标数据库为0号数据库,但客户端可以通过执行SELECT命令来切换目标数据库。以下代码示例演示了客户端在0号数据库设置并读取键msg,之后切换到2号数据库并执行类似操作的过程:

127.0.0.1:6379> SET msg "hello world"
OK
127.0.0.1:6379> GET msg
"hello world"
127.0.0.1:6379> SELECT 2
OK
127.0.0.1:6379[2]> GET msg
(nil)
127.0.0.1:6379[2]> SET msg "another world"
OK
127.0.0.1:6379[2]> GET msg
"another world"

在服务器内部,客户端状态redisClient结构的db属性记录了客户端当前的目标数据库,这个属性是一个指向redisDb结构的指针:

redis.h

typedef struct redisClient {
    ……
	//记录客户端当前正在使用的数据库
    redisDb *db;
    ……
} redisClient;

redisClient.db指针指向redisServer.db数组的其中一个元素,而被指向的元素就是客户端你的目标数据库。如果某个客户端的目标数据库为1号数据库,那么这个客户端所对应的客户端状态和服务器状态之间的关系如图1-2所示

Redis实现之数据库(一)第2张

图1-2 客户端的目标数据库为1号数据库

如果这时客户端执行命令SELECT 2,将目标数据库改为2号数据库,那么客户端的状态和服务器之间的关系将更新到如图1-3

Redis实现之数据库(一)第3张

图1-3 客户端的目标数据库为2号数据库

通过修改redisClient.db指针,让它指向服务器中的不同数据库,从而实现切换目标数据库的功能,这就是SELECT命令的实现原理

数据库键空间

Redis是一个键值对数据库服务器,服务器中的每个数据库都由redis.h/redisDb结构体表示,其中,redisDb结构体的dict字典保存了数据库中的所有键值对,我们将这个字典称为键空间

redis.h

typedef struct redisDb {
	//数据库键空间,保存着数据库中所有键值对
    dict *dict;                
    ……
} redisDb;

键空间和用户所见的数据库是直接对应的:

  • 键空间的键也就是数据库的键,每个键都是一个字符串对象
  • 键空间的值也就是数据库的值,每个值可以是字符串对象、列表对象、哈希表对象、集合对象和有序集合对象中任意一种Redis对象、集合对象和有序集合对象中的任意一种Redis对象

举个栗子,如果我们在空白的数据库中执行以下命令:

127.0.0.1:6379> SET message "hello world"
OK
127.0.0.1:6379> RPUSH alphabet "a" "b" "c"
(integer) 3
127.0.0.1:6379> HSET book name "Redis in Action"
(integer) 1
127.0.0.1:6379> HSET book author "Josiah L. Carlson"
(integer) 1
127.0.0.1:6379> HSET book publisher "Manning"
(integer) 1

那么在这些命令之后,数据库键空间将会是图1-4所展示的样子:

  • alphabet是一个列表键,键的名字是一个包含字符串"alphabet"的字符串对象,键的值则是一个包含三个元素的列表对象
  • book是一个哈希表键,键的名字是一个包含字符串"book"的字符串对象,键的值则是一个包含三个键值对的哈希表对象
  • message是一个字符串键,键的名字是一个包含字符串"message"的字符串对象,键的值则是一个包含字符串"hello world"的字符串对象

Redis实现之数据库(一)第4张

图1-4 数据库键空间例子

因为数据库的键空间是一个字典,所以所有针对数据库的操作,比如添加一个键值对到数据库,或者从数据库中删除一个键值对,又或者在数据库中获取某个键值对等,实际上都是通过对键空间字典进行操作来实现,以下几个小节将分别介绍数据库的增删改查操作的实现原理

添加新键

添加一个新的键值对到数据库,实际上就是将一个新的键值对添加到键空间字典中,其中键为字符串对象,而值可以是任意一种类型的Redis对象。举个栗子,如果键空间当前的状态如图1-4所示,那么在执行以下命令后:

127.0.0.1:6379> SET date "2013.12.1"
OK

键空间将添加一个新的键值对,这个新键值对的键是一个包含字符串"date"的字符串对象,而值对象则是一个包含字符串"2013.12.1"的字符串对象,如图1-5所示

Redis实现之数据库(一)第5张

图1-5 添加date键之后的键空间

删除键

删除数据库中的一个键,实际上就是在键空间删除一个键值对对象。举个栗子,如果键空间当前的状态如1-4所示,那么执行以下命令后:

127.0.0.1:6379> DEL book
(integer) 1

键book以及它的值将从键空间中被删除,如图1-6所示

Redis实现之数据库(一)第6张

图1-6 删除book键之后的键空间

更新键

对一个数据库键进行更新,实际上就是对键空间里面键所对应的值对象进行更新,根据值对象的类型不同,更新的具体方法也会有所不同。举个例子,如果键空间当前状态如图1-4所示,那么在执行以下命令后:

127.0.0.1:6379> SET message "blah blah"
OK

键message的值对象将从之前包含"hello world"字符串更新为"blah blah"字符串,如图1-7所示

Redis实现之数据库(一)第7张

图1-7 使用SET命令更新message键

再举个例子,如果我们继续执行以下命令:

127.0.0.1:6379> HSET book page 320
(integer) 1

那么键空间中book键的值对象(一个哈希键)将被更新,新的键值对page和320会被添加到值对象中,如图1-8所示

Redis实现之数据库(一)第8张

图1-8 使用HSET更新book

对键取值

对一个数据库键进行取值,实际上就是在键空间中取出键所对应的值对象,根据值对象的类型不同,具体的取值方法也会有所不同。举个栗子,如果键空间当前的状态如图1-4所示,那么当执行以下命令时:

127.0.0.1:6379> GET message 
"hello world"

GET命令将首先在键空间中查找键message,找到键之后接着取得该键所对应的字符串对象值,之后再返回值对象所包含的字符串"hello world",取值过程如图1-9所示

Redis实现之数据库(一)第9张

图1-9 使用GET命令取值的过程

再举个例子,当执行以下命令时:

127.0.0.1:6379> LRANGE alphabet 0 -1
1) "a"
2) "b"
3) "c"

LRANGE命令将首先在键空间查找键alphabet,找到键之后接着取得该键所对应的列表对象值,之后再返回列表对象中包含的三个字符串对象的值,取值过程如图1-10所示

Redis实现之数据库(一)第10张

图1-10 使用LRANGE命令取值的过程

其他键空间操作

除了上面列出的添加、删除、更新、取值操作外,还有很多针对数据库本身的Redis命令,也是通过对键空间进行处理来完成的。比如说,用于清空整个数据库的FLUSHDB命令,就是通过删除键空间中的所有键值对来实现的。又比如说,用于随机返回数据库中某个键的RANDOMKEY命令,就是通过在键空间中随机返回一个键来实现的。另外,用于返回数据库键数量的DBSIZE命令,就是通过返回键空间中包含的键值对的数量来实现的。类似还有EXISTS、RENAME、KEYS等,这些命令都是通过对键空间进行操作来实现的

读写键空间的维护操作

当使用Redis命令对数据库进行读写时,服务器不仅会对键空间执行指定的读写操作,还会执行一些额外的维护操作,其中包括:

  • 在读取一个键之后(读操作和写操作都要对键进行读取),服务器会根据键是否存在来更新服务器的键空间命中次数或键空间不命中次数,这两个值可以在INFO stats命令的keyspace_hits属性和keyspace_misses属性中查看
  • 在读取一个键之后,服务器会更新键的LRU(最后一次使用)时间,这个值可以用于计算键的闲置时间,使用OBJECT idletime<key>命令可以查看键key的闲置时间
  • 如果服务器在读取一个键时发现该键已过期,那么服务器会先删除这个过期键,然后才执行余下的其他操作
  • 如果客户端使用WATCH命令监视某个键,那么服务器在对被监视的键进行修改之后,会将这个键标记为脏,从而让事物程序注意到这个键已经被修改了
  • 服务器每次修改一个键之后,都会对脏键计数器的值加1,这个计数器会触发服务器的持久化以及复制操作
  • 如果服务器开启了数据库通知功能,那么在对键进行修改之后,服务器将按配置发送相应的数据库通知

免责声明:文章转载自《Redis实现之数据库(一)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇U盘格式和对齐扇数(常识)保险业务核心系统设计参考下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

WordPress快速打造个人博客

前些天用wordpress搭建了现在这个博客,所以总结了一篇文章,讲讲怎么样简单的创建一个博客。开始前这里有篇我搭建时所遇到的问题可以作为参考《WordPress建站注意事项》,首先我们要下载WordPress压缩包(这个建议到官网上下最新的保险些:https://cn.wordpress.org/),下载下来是长这个样子的   ,   之后我们要通过 “...

Docker Swarm 服务版本更新与回滚

Docker Swarm 服务版本更新 环境: 系统:Centos 7.4 x64 应用版本:Docker 18.09.0 管理节点:192.168.1.79 工作节点:192.168.1.78 工作节点:192.168.1.77 1、管理节点:创建服务设置更新策略 docker service create --replicas 3 --na...

在SpringBoot中使用flyway进行数据库版本管理

本文大纲 flyway是什么 能帮助我们解决什么问题 springboot环境下使用flyway flyway的工作原理 一、flyway是什么 Flyway是一个开源的数据库版本管理工具,并且极力主张“约定大于配置”,简单、专注、强大。可以使用SQL完成数据同步,或者基于特定数据库的语法(例如PL / SQL,T-SQL等)或Java代码(适用于高...

Redis缓存数据库安全加固指导(一)

背景 在众多开源缓存技术中,Redis无疑是目前功能最为强大,应用最多的缓存技术之一,参考2018年国外数据库技术权威网站DB-Engines关于key-value数据库流行度排名,Redis暂列第一位,但是原生Redis版本在安全方面非常薄弱,很多地方不满足安全要求,如果暴露在公网上,极易受到恶意攻击,导致数据泄露和丢失。 本文主要是在原生开源软件Red...

数据库打开时报错该如何解决

故障描述 故障主要表现为打开数据库时报错,内容为:“system01.dbf需要更多的恢复来保持一致性,数据库无法打开”。经过对数据库文件的检测后初步可得出以下结论:sysaux01.dbf有坏块,sysaux01.dbf文件损坏。急需恢复zxfg用户下的数据。 故障分析出现上述报错的可能性原因主要有控制文件损坏、数据文件损坏,数据文件与控制文件的SCN不...

Oracle数据库之二 Oracle的下载与安装

二、Oracle 的下载与安装 2.1、Oracle 简介 Oracle 公司是全球最大的信息管理软件及服务供应商,成立于 1977 年,主要的业务是推动电子商务平台的搭建。Oracle 公司有自己的服务器、数据库、开发工具、编程语言,在行业软件上还有企业资源计划(ERP)软件、客户关系管理(CRM)软件、人力资源管理软件(HCM)等大型管理系统,所以...