前面我们花了很多的时间介绍了 redis 中基本的数据结构,及其内部的实现情况,这些都是非常基础的东西,可能不经意间你就会用到他们,希望你花点时间了解一下。

接下来,我们将走近 redis 数据库,学习各种操作 redis 的命令,并介绍它的一些实现策略以及集群配置等等内容。

一、redis 中的数据库

server.h/redisServer 结构中有一个字段,db 字段:

redisDb *db;

db 被定义成一个 redisDb 数组,其中 redisDb 的定义如下:

typedef struct redisDb {
dict *dict;
dict *expires;
dict *blocking_keys;
dict *ready_keys;
dict *watched_keys;
int id;
long long avg_ttl;
} redisDb;

我们经常说 redis 具有十六个数据库,可以切换不通的数据库做数据隔离,这里你就可以将一个 redisDb 实例理解为一个数据库,而 db 指针则可以访问 redis 预定义的所有数据库。

其中,dict 是一个字典结构,用于实际存储数据,expires 也是一个字典结构,它存储的是数据库中所有设置过期时间的键值对,保存他们的过期时间,是一个 UNIX 时间戳。

blocking_keys 存储了被阻塞的键集合,ready_keys 存储的是可以解除阻塞的键集合,watched_keys 存储的是正在被 watch 命令监控的键的集合,id 记录的是当前数据库的一个编号,avg_ttl 收集了所有键剩余存活时间的一个平均值。这一部分的几个字段的值这里不做详细的解释,没有基本的知识储备,不容易理解的,后面我们还会遇到的。

server.h/redisServer 结构中还有一个字段,dbnum 字段:

int dbnum;  /* Total number of configured DBs */

与此同时,redis.conf 配置文件中,默认有这么一项配置。

所以,我们启动 redis-server 的时候,会根据配置文件中给定的配置默认创建 16 个数据库。

1、select 命令

select 命令用于我们切换数据库,例如:

默认连接上 redis-server 的客户端使用 0 号数据库,鉴于 redis 并没有提供给客户端查询当前使用数据库编号的命令,所以建议执行 redis 命令之前,尤其是修改、添加命令,先执行下切换数据库的命令。

2、set 命令

set 命令其实无需过多介绍,它向数据库中添加一个键值对,大部分情况下,键会是一个字符串对象,而值可取我们 redis 的五大对象之一。

因为 redisDb 底层是字典结构,键不允许重复,故而 set 命令同样适用于更新操作。

3、del 命令

del 命令用于删除数据库中一个键值对,标准语法如下:

del [KEY]

例如:

4、get 命令

get 命令用于获取一个键值对的值,标准语法是:

get [KEY]

具体事例就不再演示了,get 命令对应于 set 命令,只能获取由 set 命令添加的字符串对象键值对,而例如一些集合对象,或列表对象需要用类似于 sadd、zadd 等命令进行数据库的添加,自然 get 命令也是无法得到这些键值对对象值的。

5、其他命令

第一个我们介绍 dbsize 命令,它返回当前数据库有多少键值对,基本语法格式如下:

dbsize

第二个我们要介绍的是 flushdb 命令,它用于清空当前数据库,这是一个非常危险的命令,谨慎使用,基本语法格式如下:

flushdb

第三个我们要介绍一个 kyes 命令,它会返回数据库中所有符合匹配规则的键的集合,这个规则起初我以为是正则表达式,几番操作后发现匹配不上,查阅资料貌似不是正则,并且仅有以下三种规则:

  1. ?:用于匹配单个字符
  2. *:用于匹配零个或者多个字符
  3. []:可以用来指定模式的选择区间

正则表达式中的问号,用于匹配前一个字符出现零次或一次,即要么出现要么不出现,而我们这里的 keys 模式,问号具有不同的意义。

不过 keys 命令这种遍历整个数据库的命令是非常耗 CPU 的,可能会导致 redis 性能下降,不做推荐使用。

最后还有一个简单的命令就是 exists,它用于判断给定的 key 是否存在,返回 0 说明不存在,返回 1 说明存在。

二、过期键

有的时候我们希望给某些键一个过期时间,即希望它存活一段时间就失效,我们 redis 为我们提供了这样的机制。

1、expire

expire 用于设定某个键的过期时间,单位是秒,基本语法格式如下:

expire [key] [time]

可以看到,五秒后 redis 为我们删除了键 hello。与之对应的有一个命令 pexpire,他会将我们传入的 time 参数解析为毫秒,例如 「pexpire hello 5」会将键通过五毫秒之后删除。

2、expireat

expireat 用于设定某个键在某个具体 Unix 时间戳过期,单位秒,基本语法如下:

expireat [key] [time]

过期键会在我们指定 Unix 时间戳别删除。当然它也有一个对应毫秒单位的命令,pexpireat ,他会解析命令参数时间戳为毫秒,也就是你需要传入的时间戳不再是秒单位的,而是毫秒单位的时间戳。

3、ttl 和 pttl

这两个命令用于查看某个过期键还剩余多少时间,基本语法格式如下:

ttl/pttl [key]

ttl 命令输出的单位是秒,pttl 输出的单位是毫秒,仅此区别。

4、persist

persist 用于移除某个过期键的过期时间,也就是让这个键成为永久有效键。基本语法格式如下:

persist [key]

以上就是 redis 中过期键相关的命令,之前也说过,redisDb 数据结构中有一个 expires 字典,它存储的就是库中所有过期键以及他们生存截止时间。

那么,你可能会好奇了,redis 只记录了某个过期键的截止有效时间,那么什么时候删除这些过期键呢?

三、过期键删除策略

因为 redis 通过 expires 字典记录所有的过期键以及他们的过期时间(一个 Unix时间戳),那么我们只需要比对当前系统时间戳 Unix 是否大于键的过期时间戳即可判断键是否过期。

我们直接介绍 redis 使用的两种删除策略,定期删除和惰性删除。

  1. 定期删除:redis 每间隔一段时间进行一次小规模,有限时长的删除过期键操作。
  2. 惰性删除:redis 每次读取某个键的时候,会先判断这个键是否过期,如果过期,执行删除操作。

这两个策略,每一个都有缺点,定期删除需要每间隔一段时间触发一次删除,所以需要用户对系统的业务量、请求峰谷点有熟悉的的了解,才能配置合适的频率,否则过于高频会平白增加 CPU 压力,过于低频会导致内存中过多无用内存占用,降低系统性能。

而惰性删除缺点非常直接,如果某些键过期了,且程序永远不会访问这些键,那么 redis 就永远不会释放这些键占用的内存,进而导致内存泄漏。

redis 中同时使用了这两种删除策略,一方面,每次读取键的时候会调用方法 db.c/expireIfNeeded 判断当前键是否过期,如果过期则删除并返回 nil。另一方面,redis 中有一个定期的时间事件函数,server.c/serverCron,每次执行都会收集与更新一些与服务器状态相关的信息,比如更新服务器时间、计算对象空转时长,管理客户端连接资源的释放等等。

其中也会执行一个 expire.c/activeExpireCycle 函数,默认配置了一千微妙内返回,activeExpireCycle 会在时间内选定数据库,随机的处理这些过期键,并给予删除。

所以,其实上 redis 通过这两种策略的结合,定期删除保证不存在某些过期键永远得不到删除以进而引发内存泄漏,惰性删除使得 redis 不用集中大量时间处理这些过期键以引起 CPU 负载过大。

下一节,我们讲 redis 如何做持久化存储,毕竟数据放在内存,一旦服务器宕机、断点,所有数据都会丢失,所以我们也需要将数据备份磁盘。下节见~


关注公众不迷路,一个爱分享的程序员。

公众号回复「1024」加作者微信一起探讨学习!

每篇文章用到的所有案例代码素材都会上传我个人 github

https://github.com/SingleYam/overview_java

欢迎来踩!

Redis 中的数据库的更多相关文章

  1. Redis | 第4章 Redis中的数据库《Redis设计与实现》

    目录 前言 1. Redis中的数据库 2. 数据库的键空间 3. 键的生成时间与过期时间 4. Redis中的过期键删除策略 5. AOF.RDB和复制功能对过期键的处理 5.1 生成 RDB 文件 ...

  2. Redis中单机数据库的实现

    1. 内存操作层 zmalloc 系接口 redis为了优化内存操作, 封装了一层内存操作接口. 默认情况下, 其底层实现就是最简朴的libc中的malloc系列接口. 如果有定制化需求, 可以通过配 ...

  3. 深入理解Redis中的主键失效及其实现机制

    参考:http://blog.sina.com.cn/s/articlelist_1221155353_0_1.html 作为一种定期清理无效数据的重要机制,主键失效存在于大多数缓存系统中,Reids ...

  4. PHP中的数据库三、redis

    h2:first-child, body>h1:first-child, body>h1:first-child+h2, body>h3:first-child, body>h ...

  5. Redis中的批量删除数据库中的Key

    本文参考:http://blog.csdn.net/spring21st/article/details/15771861 http://stackoverflow.com/questions/575 ...

  6. 22、redis中数据库默认是多少个db 及作用?

    redis下,数据库是由一个整数索引标识,而不是由一个数据库名称.默认情况下,一个客户端连接到数据库0.redis配置文件中下面的参数来控制数据库总数: /etc/redis/redis.conf 文 ...

  7. 如何将redis中的数据导入到本地MongoDB和MySQL数据库

    将redis中的数据导入到本地MongoDB数据库 创建一个process_items_mongodb.py文件(文件名自定义): #!/usr/bin/env python # -*- coding ...

  8. 多线程查询数据,将结果存入到redis中,最后批量从redis中取数据批量插入数据库中【我】

    多线程查询数据,将结果存入到redis中,最后批量从redis中取数据批量插入数据库中 package com.xxx.xx.reve.service; import java.util.ArrayL ...

  9. Redis使用场景一,查询出的数据保存到Redis中,下次查询的时候直接从Redis中拿到数据。不用和数据库进行交互。

    maven使用: <!--redis jar包--> <dependency> <groupId>redis.clients</groupId> < ...

随机推荐

  1. 通俗易懂了解Vue中nextTick的内部实现原理

    1. 前言 nextTick 是 Vue 中的一个核心功能,在 Vue 内部实现中也经常用到 nextTick.在介绍 nextTick 实现原理之前,我们有必要先了解一下这个东西到底是什么,为什么要 ...

  2. AngularJS: Error reports on $injector:modulerr

    Angular JS最常见的问题是,程序启动失败,error为$injector:modulerr 错误是因为加载对应的Module失败,但很难找到需要修改的Module. 一个简单的小技巧是,不要使 ...

  3. 通过 position:fixed 实现底部导航

    通过 position:fixed 实现底部导航 HTML <div id="footer">页脚</div> CSS #footer { clear: b ...

  4. lqb 基础练习 十六进制转十进制

    基础练习 十六进制转十进制 时间限制:1.0s   内存限制:512.0MB     问题描述 从键盘输入一个不超过8位的正的十六进制数字符串,将它转换为正的十进制数后输出. 注:十六进制数中的10~ ...

  5. Vue_声明周期

    Vue生命周期 在vue2.0的时候,声明钩子发生了改变,具体有八个 <!-- HTML部分 --> <div id="app"> <div>{ ...

  6. python2的编码问题小结

    对于python2,经常会遇到编码问题,在此小记一下. Python2默认的编码解码方式是ascii码,这点要牢记. windows系统默认是gbk编码的,可以使用chcp查看:936,那就是GBK简 ...

  7. vue cli3.0 封装组件全局引入js文件并发布到npm

    首先用 vue create创建一个项目 当前的项目目录是这样的: 首先需要创建一个 packages 目录,用来存放组件 然后将 src 目录改为 examples 用作示例 二.修改配置 启动项目 ...

  8. DNS name

    DNS name 指的反向解析的域名.

  9. Golang 入门系列(十六)锁的使用场景主要涉及到哪些?读写锁为什么会比普通锁快

    前面已经讲过很多Golang系列知识,感兴趣的可以看看以前的文章,https://www.cnblogs.com/zhangweizhong/category/1275863.html, 接下来要说的 ...

  10. 实现自定义的参数解析器——HandlerMethodArgumentResolver

    1.为什么需要自己实现参数解析器 我们都知道在有注解的接口方法中加上@RequestBody等注解,springMVC会自动的将消息体等地方的里面参数解析映射到请求的方法参数中. 如果我们想要的信息不 ...