Redis 附加功能(三)— 持久化、发布与订阅及模块
Redis 把所有数据都存储在内存中,而传统数据库将实际的数据存储在硬盘中。Redis的持久化功能把内存中存储的数据以文件形式存储到硬盘上,服务器也可以根据这些文件在系统停机之后实施数据恢复。
发布与订阅功能可以让客户端通过广播方式,将消息同时发送给可能存在的多个客户端。
模块功能允许开发者通过Redis开放的API,通过C语言在Redis之上构建任意复杂的、全新的数据结构、功能和应用。
1 持久化
Redis提供了RDB持久化、AOF持久化和RDB-AOF混合持久化等多种持久化方式。也可以完全关闭持久化功能。
1.1 RDB持久化
RDB、Redis DataBase(Redis 数据库),构建出一个经过压缩的二进制文件(包含了服务器在各个数据库中存储的键值对数据等信息),文件名一般为dump.rdb。
全量式持久化。
1.1.1 创建方式
SAVE | 阻塞服务器,并创建PDB文件。 在命令执行期间,Redis服务器将阻塞,直到RDB文件创建完毕为止。 |
BGSAVE | 以非阻塞方式创建PDB文件。 命令以异步方式执行,会将命令执行之前的数据保存到RDB中,创建中及之后的数据不会被保存。 |
自动创建 | 通过save选项配置。 save <seconds> <changes> 如果服务在seconds秒内至少进行changes次修改,服务器将自动执行一次BGSAVE命令。 可以同时使用多个save选项。 默认配置: save 60 10000 save 300 100 save 3600 1 注意:每次成功创建RDB文件之后,时间计数器及修改次数计数器都会被清零。 |
表 创建RDB的方式
1.1.2 优缺点
优点 | RDB文件体积小,恢复数据的速度快。 |
缺点 |
|
表 RDB方式的优缺点
1.2 AOF持久化
增量式持久化。服务器每次执行完命令后,都会以协议文本的方式将被执行的命令(非只读命令)追加到AOF文件的结尾。
在配置文件中打开AOF持久化功能:appendonly yes
1.2.1 AOF文件的冲洗频率
为了提供程序的写入性能,操作系统通常会把针对硬盘的多次写操作优化为一次写操作。当程序调用操作系统接口对文件进行写入时,系统不会直接把数据写入硬盘,而是会将数据写入位于内存的缓冲区中,等到指定的时限到达或满足某些条件时,才将缓冲区的数据冲洗至硬盘。
这种机制虽提高了程序性能,但给写入操作带来了不确定性(当系统突然停机时,内存缓冲区中的数据可能会丢失)。Redis提供了appendfsync <value>选项,控制系统冲洗AOF文件的频率。
always | 每执行一个写命令,就对AOF文件执行一次冲洗 |
everysec | 默认值,每隔1s,就对AOF文件执行一次冲洗 |
no | 不主动对AOF文件执行冲洗,由操作系统决定。 |
表 appendfsync 的选项
1.2.2 AOF重写
如果服务器对相同的键执行过多次修改操作,那么AOF文件会出现多个冗余命令。减少冗余,取最后一次对这个键的写操作来存储。 这时,需要对AOF文件进行重写。
BGREWRITEAOF | 显式触发AOF重写操作,是一个异步命令。 注意:1)发送BGREWRITEAOF命令时,如果服务器正在持久RDB文件,那么AOF重写操作会等RDB文件创建完毕之后再执行。2)如果再执行重写操作过程中,又接收到BGREWRITEAOF,那服务器会返回错误。 |
AOF重写配置选项 | auto-aof-rewrite-min-size <value> 设置触发自动重写需要的最小AOF文件体积。 auto-aof-rewrite-percentage <value> 触发自动重写所需的文件增大的百分比。与上次AOF文件重写之后的体积相比(如果没执行过重写,那么使用启动服务器时使用的AOF文件体积)。 |
表 触发AOF 重写的方式
1.2.3 优缺点
优点 | 丢失数据更少 |
缺点 |
|
表 AOF的优缺点
1.3 RDB-AOF混合
配置打开RDB-AOF混合持久方式:aof-use_rdb-preamble yes
原理:在执行AOF重写时,根据数据库当前的状态生成出相应的RDB数据,并将这些数据写入新建的AOF文件中,那些在AOF重写之后执行的命令,继续以协议文本的方式追加到新AOF文件的末尾(即已有的RDB数据后面)。
1.4 其他
1.4.1 单纯的内存缓存服务器
启动服务时,配置 save “”
1.4.2 SHUTDOWN:关闭服务器
默认情况下,在执行这个命令时,会根据服务器的持久化配置选项,决定是否执行数据保存操作。
2 发布与订阅
图 Redis 的频道和它的订阅者
客户端与频道的对应关系是多对多。客户端可以订阅频道模式,模式匹配到的频道都会发送消息给该客户端。
客户端可以将消息发布到多个频道。
2.1 相关命令
发布消息 | PUBLISH channel message |
订阅 | SUBSCRIBE channel [channel ...] 在redis-cli 执行订阅命令之后,就会进入阻塞状态,无法再执行其他任何命令,在阻塞过程中,监听频道发送的消息。 只能强制退出redis-cli程序。 PSUBSCRIBE pattern [pattern ...] 订阅模式 |
退订 | UNSUBSCRIBE [channel ...] 当不指定频道时,会退订所有频道。 PUNSUBSCRIBE [pattern ...] 退订模式 |
查看发布与订阅 PUBSUB | PUBSUB CHANNELS [pattern] 查看频道 |
PUBSUB NUMSUB [channel ...] 查看任意多个给定频道的订阅者数量。 | |
PUBSUB NUMPAT 被订阅模式的总数量。 |
表 发布与订阅相关命令
2.2 示例
广播系统:公司A 及 公司B会不定时发布消息,证券公司、股民能接收到这些消息。
public class SecuritiesMarket {
private static class CompanyThread extends Thread {
private final String name;
private final String channel;
private CompanyThread(String name, String channel) {
this.name = name;
this.channel = channel;
}
@Override
public void run() {
Jedis redis = RedisPool.getRedis();
Random random = new Random();
int count = 0;
while (true) {
redis.publish(channel,name + "利好:" + (count++));
try {
Thread.sleep((random.nextInt(5) + 1) * 1000L);
} catch (InterruptedException e) {
System.err.println(name + "利好释放完毕");
break;
}
}
}
}
public static void main(String[] args) {
CompanyThread threadA = new CompanyThread("A","company_a");
CompanyThread threadB = new CompanyThread("B","company_b");
threadA.start();
threadB.start();
JedisPubSub personnel = new JedisPubSub() {
@Override
public void onMessage(String channel, String message) {
System.err.println("股民接收到消息," + channel + ":" + message);
}
};
JedisPubSub securityCompany = new JedisPubSub() {
@Override
public void onMessage(String channel, String message) {
System.out.println("证券公司接收到消息," + channel + ":" + message);
}
};
Runnable run1 = () -> {
RedisPool.getRedis().subscribe(personnel,"company_b");
};
Runnable run2 = () -> {
RedisPool.getRedis().subscribe(securityCompany,"company_a","company_b");
};
new Thread(run1).start();
new Thread(run2).start();
}
}
3 模块
Redis为用户提供了流水线、事务及Lua脚本用于扩展功能,但是它们有几个明显的缺陷:
- 必须基于Redis现有的数据结构或功能来实现。
- 在编程方面过于复杂。
- 性能上有损耗。
模块功能允许开发者基于Redis提供的API,通过C语言在Redis之上构建复杂的、全新的数据结构及功能,让使用者用“原生”方式在Redis调用及使用扩展的功能及数据结构,而且在性能上带来了更大的提升。
3.1 管理模块
图 模块的生命周期
编译模块:使用make命令
载入模块:配置项 loadmodule <module_path> 或使用命令 MODULE LOAD module_path
列出已载入的模块:MODULE LIST
卸载模块: MODULE UNLOAD module_name
3.2 热门模块
ReJSON | 实现了Redis对JSON数据的原生支持。用户可以直接在Redis中存储、更新和获取JSON数据。 |
RediSQL | 嵌入了一个完整的SQLite实现,用户可以在Redis中使用SQLite的全部功能,可以使用SQL语句。 |
RediSearch | 全文搜索引擎,为多种语言的文档建立索引。 |
表 Redis 热门模块