前面我们花了很多的时间介绍了 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. 【gradle使用—gradle介绍】

    Gradle基本组件  Gradle是一个框架,定义了一套自己的规则,所以要搞清楚Gradle,必须要遵守它设计的原则,下面我们要先搞清楚一些Gradle的概念: Gradle中,每一个待编译的工程是 ...

  2. Go 基础学习笔记 (5)| 数据类型说明与使用

    在 Go 编程语言中,数据类型用于声明函数和变量. 数据类型的出现是为了把数据分成所需内存大小不同的数据,编程的时候需要用大数据的时候才需要申请大内存,就可以充分利用内存. Go 语言按类别有以下几种 ...

  3. 创建windows系统下的虚拟机

    创建新的虚拟机  添加映像(windows系统下必须先添加映像) 选择相应的镜像文件:网上查找密钥输入:版本按要求选择:单击 下一步:设置虚拟机名称  位置 :下一步 默认的磁盘大小(不用管)——下一 ...

  4. JavaScript部分案例

    JavaScript 是 Web 的编程语言. 所有现代的 HTML 页面都使用 JavaScript. JavaScript 非常容易学. 阅读本教程,您需要有以下基础: HTML 教程 CSS 教 ...

  5. Linux系统重启Oracle-12c步骤

    Linux系统重启Oracle-12c步骤 1. 使用oracle用户登录: [root@Oracle-12c /]# su – oracle 2. 登录oracle登陆管理员账号: [oracle@ ...

  6. Python文件打包exe教程——Pyinstaller(亲测有效)

    今天将要解决一个问题,如何打包Pyhon文件 众所周知,Python文件的后缀名为“.py” 所以执行Python文件的要求之一便是具有python环境. 偶尔特殊情况,或者运行一些比较简单的工具,但 ...

  7. Java升级那么快,多个版本如何灵活切换和管理?

    前言 近两年,Java 版本升级频繁,感觉刚刚掌握 Java8,写本文时,已听到 java14 的消息,无论是尝鲜新特性(Java12 中 Collectors.teeing 超强功能使用),还是由于 ...

  8. PHP 发送get请求

    PHP 发送get请求 file_get_contents 方法: $s = file_get_contents("http://apis.map.qq.com/ws/distance/v1 ...

  9. BeautifulSoup随笔

    BeautifulSoup是一个类 b = BeautifulSoup(html) b对象有与html结构相关的各种方法和和属性. a = b.findAll('a')获得标签的对象 a对象又有关于属 ...

  10. git操作忽略.iml文件

    git操作忽略.iml文件** 参考:https://blog.csdn.net/m0_38001814/article/details/87354584 因为.iml文件的修改导致代码pull失败 ...