redis实现数据库(一)
转:https://www.cnblogs.com/beiluowuzheng/p/9738159.html
服务器中的数据库
Redis服务器将所有数据库都保存在服务器状态redis.h/redisServer结构体的db数组中,db数组的每个项都是一个redis.h/redisDb结构体,每个redisDb结构体代表一个数据库
redis.h
1
2
3
4
5
6
|
struct redisServer { …… //一个数组,保存着服务器中所有数据库 redisDb *db; …… }; |
在初始化服务器时,程序会根据服务器状态的dbnum属性来决定应该创建多少个数据库:
redis.h
1
2
3
4
5
6
|
struct redisServer { …… //服务器的数据库数量 int dbnum; …… }; |
dbnum属性由服务器配置的database选项决定,默认情况下,该选项的值为16,所以Redis服务器默认会创建16个数据库,如图1-1所示
图1-1 服务器数据库示例
切换数据库
每个Redis客户端都有自己的目标数据库,每当客户端执行数据库写命令或者数据库读命令的时候,目标数据库就会成为这些命令的操作对象。默认情况下,Redis客户端的目标数据库为0号数据库,但客户端可以通过执行SELECT命令来切换目标数据库。以下代码示例演示了客户端在0号数据库设置并读取键msg,之后切换到2号数据库并执行类似操作的过程:
1
2
3
4
5
6
7
8
9
10
11
12
|
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
1
2
3
4
5
6
|
typedef struct redisClient { …… //记录客户端当前正在使用的数据库 redisDb *db; …… } redisClient; |
redisClient.db指针指向redisServer.db数组的其中一个元素,而被指向的元素就是客户端你的目标数据库。如果某个客户端的目标数据库为1号数据库,那么这个客户端所对应的客户端状态和服务器状态之间的关系如图1-2所示
图1-2 客户端的目标数据库为1号数据库
如果这时客户端执行命令SELECT 2,将目标数据库改为2号数据库,那么客户端的状态和服务器之间的关系将更新到如图1-3
图1-3 客户端的目标数据库为2号数据库
通过修改redisClient.db指针,让它指向服务器中的不同数据库,从而实现切换目标数据库的功能,这就是SELECT命令的实现原理
数据库键空间
Redis是一个键值对数据库服务器,服务器中的每个数据库都由redis.h/redisDb结构体表示,其中,redisDb结构体的dict字典保存了数据库中的所有键值对,我们将这个字典称为键空间
redis.h
1
2
3
4
5
|
typedef struct redisDb { //数据库键空间,保存着数据库中所有键值对 dict *dict; …… } redisDb; |
键空间和用户所见的数据库是直接对应的:
- 键空间的键也就是数据库的键,每个键都是一个字符串对象
- 键空间的值也就是数据库的值,每个值可以是字符串对象、列表对象、哈希表对象、集合对象和有序集合对象中任意一种Redis对象、集合对象和有序集合对象中的任意一种Redis对象
举个栗子,如果我们在空白的数据库中执行以下命令:
1
2
3
4
5
6
7
8
9
10
|
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"的字符串对象
图1-4 数据库键空间例子
因为数据库的键空间是一个字典,所以所有针对数据库的操作,比如添加一个键值对到数据库,或者从数据库中删除一个键值对,又或者在数据库中获取某个键值对等,实际上都是通过对键空间字典进行操作来实现,以下几个小节将分别介绍数据库的增删改查操作的实现原理
添加新键
添加一个新的键值对到数据库,实际上就是将一个新的键值对添加到键空间字典中,其中键为字符串对象,而值可以是任意一种类型的Redis对象。举个栗子,如果键空间当前的状态如图1-4所示,那么在执行以下命令后:
1
2
|
127.0.0.1:6379> SET date "2013.12.1" OK |
键空间将添加一个新的键值对,这个新键值对的键是一个包含字符串"date"的字符串对象,而值对象则是一个包含字符串"2013.12.1"的字符串对象,如图1-5所示
图1-5 添加date键之后的键空间
删除键
删除数据库中的一个键,实际上就是在键空间删除一个键值对对象。举个栗子,如果键空间当前的状态如1-4所示,那么执行以下命令后:
1
2
|
127.0.0.1:6379> DEL book (integer) 1 |
键book以及它的值将从键空间中被删除,如图1-6所示
图1-6 删除book键之后的键空间
更新键
对一个数据库键进行更新,实际上就是对键空间里面键所对应的值对象进行更新,根据值对象的类型不同,更新的具体方法也会有所不同。举个例子,如果键空间当前状态如图1-4所示,那么在执行以下命令后:
1
2
|
127.0.0.1:6379> SET message "blah blah" OK |
键message的值对象将从之前包含"hello world"字符串更新为"blah blah"字符串,如图1-7所示
图1-7 使用SET命令更新message键
再举个例子,如果我们继续执行以下命令:
1
2
|
127.0.0.1:6379> HSET book page 320 (integer) 1 |
那么键空间中book键的值对象(一个哈希键)将被更新,新的键值对page和320会被添加到值对象中,如图1-8所示
图1-8 使用HSET更新book
对键取值
对一个数据库键进行取值,实际上就是在键空间中取出键所对应的值对象,根据值对象的类型不同,具体的取值方法也会有所不同。举个栗子,如果键空间当前的状态如图1-4所示,那么当执行以下命令时:
1
2
|
127.0.0.1:6379> GET message "hello world" |
GET命令将首先在键空间中查找键message,找到键之后接着取得该键所对应的字符串对象值,之后再返回值对象所包含的字符串"hello world",取值过程如图1-9所示
图1-9 使用GET命令取值的过程
再举个例子,当执行以下命令时:
1
2
3
4
|
127.0.0.1:6379> LRANGE alphabet 0 -1 1) "a" 2) "b" 3) "c" |
LRANGE命令将首先在键空间查找键alphabet,找到键之后接着取得该键所对应的列表对象值,之后再返回列表对象中包含的三个字符串对象的值,取值过程如图1-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实现数据库(一)的更多相关文章
- Redis 与 数据库处理数据的两种模式
Redis 是一个高性能的key-value数据库. redis的出现,很大程度补偿了memcached这类key-value存储的不足,在部 分场合可以对关系数据库起到很好的补充作用.它提供了Pyt ...
- 快速搭建Redis缓存数据库
之前一篇随笔——Redis安装及主从配置已经详细的介绍过Redis的安装于配置.本文要讲的是如何在已经安装过Redis的机器上快速的创建出一个新的Redis缓存数据库. 一.环境介绍 1) Linux ...
- Redis 与 数据库处理数据的两种模式(转)
Redis 是一个高性能的key-value数据库. redis的出现,很大程度补偿了memcached这类key-value存储的不足,在部 分场合可以对关系数据库起到很好的补充作用.它提供了Pyt ...
- Redis与数据库同步问题
缓存数据与持久化数据的一致性,这个问题总结了一下(看到了一个不错的博文),其实就是读和写,还有就是要注意谁先谁后的问题. Redis 是一个高性能的key-value数据库. redis的出现,很大程 ...
- Redis 当成数据库在使用和可靠的分布式锁,Redlock 真的可行么?
怎样做可靠的分布式锁,Redlock 真的可行么? https://martin.kleppmann.com/2016/02/08/how-to-do-distributed-locking.html ...
- Redis和数据库 数据同步问题
Redis和数据库同步问题 缓存充当数据库 比如说Session这种访问非常频繁的数据,就适合采用这种方案:当然了,既然没有涉及到数据库,那么也就不会存在一致性问题: 缓存充当数据库热点缓存 读操作 ...
- [技术博客] 用户验证码验证机制---redis缓存数据库的使用
目录 问题引入 初识redis 实际应用 作者:马振亚 问题引入 在这次的开发过程中,我们的需求中有一个是普通用户可以通过特定的机制申请成为社长.因为只有部分人才能验证成功,所以这个最开始想了两种思路 ...
- 如何保证Redis与数据库的数据一致性
一般来说,只要你用到了缓存,不管是Redis还是memcache,就可能会涉及到数据库缓存与数据的一致性问题,这里我们以Redis为例. 我们该如何保证Redis与数据库的一致性呢? So easy: ...
- Django缓存机制以及使用redis缓存数据库
目录 Django 配置缓存机制 缓存系统工作原理 Django settings 中 默认cache 缓存配置 利用文件系统来缓存 使用Memcache来缓存: 使用Local-memory来缓存: ...
- Redis单机数据库的实现原理
本文主要介绍Redis的数据库结构,Redis两种持久化的原理:RDB持久化.AOF持久化,以及Redis事件分类及执行原理.最后,分别介绍了单机班Redid客户端和Redis服务器的使用和实现原理. ...
随机推荐
- Javascript 表达式中连续的 && 和 || 之赋值区别
为了区分赋值表达式中出现的连续的 ‘&&’和 ‘||’的不同的赋值含义,做了一个小测试,代码如下: function write(msg){ for(var i = 0; i ...
- mongodb Map/reduce测试代码
private void AccountInfo() { ls.Clear(); DateTime dt = DateTime.Now.Date; IMongoQuery query = Query& ...
- Java的同步和异步
同步:发送一个请求,等待返回,然后再发送下一个请求 异步:发送一个请求,不等待返回,随时可以再发送下一个请求 同步可以避免出现死锁,读脏数据的发生,一般共享某一资源的时候用,如果每个人都有修改权限,同 ...
- SQLite数据库迁移MySQL(MariaDB)完整步骤
第一步(SQLite导出数据库): 命令方式导出数据库 > .output d:/data/lagou.sql //导出路径及文件名 > .dump //开始导出 修改lagou.sql文 ...
- axios统一封装
本文代码参考了网上别人的资料,经过修改而来 /** * Created by zxf on 2017/9/6. * 封装统一的ajax请求,统一拦截请求,对不同的请求状态封装 * 通常说, ajax ...
- ERROR 1129 (00000) Host ‘XXXXXX’ is blocked because of many connection errors; unblock with ‘mysqlad
1.今天早上由于公司网络带宽达到上限,导致多台web服务器连接mysql服务器超时.后来情况好转后,连接数据库服务器出现如下错误. Host '*' is blocked because of man ...
- xshell 常用命令1
date命令 date命令是显示或设置系统时间与日期. 很多shell脚本里面需要打印不同格式的时间或日期,以及要根据时间和日期执行操作.延时通常用于脚本执行过程中提供一段等待的时间.日期可以以多种格 ...
- python数据类型简介
python中的注释:注释仅仅是给人看的,python并不进行识别. 注释的分类: 单行注释:# 多行注释:用三对单引号或双引号 与用户交互: 1.python3中输入 关键字:input() pyt ...
- react router为什么推荐使用browserHistory而不推荐hashHistory?
首先 browserHistory 其实使用的是 HTML5 的 History API,浏览器提供相应的接口来修改浏览器的历史记录:而 hashHistory 是通过改变地址后面的 hash 来改变 ...
- 关于线上bug
之所以想写下线上bug,因为发觉有些公司对线上bug的处理是比较严格甚至是很苛刻,涉及到的相关人可能会因此而背黑锅. 之所以会存在这样情况,因为公司各部门都有关联,特别是用户.老板的投诉,也给公司会造 ...