Redis-事务与日志

事务

如何实现Redis原子性

Redis在执行单条命令时具有原子性,因为Redis采用单线程模型,所有的命令都在一个主线程中顺序执行,不存在多线程竞争问题。然而,如果需要执行多条命令并保持原子性,Redis提供了Lua脚本功能,可以将多条Redis命令组合成一个整体执行,从而保证操作的原子性。

假设我们有一个分布式系统,多个进程需要访问一个共享资源,我们可以使用以下Lua脚本来实现分布式锁:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-- Lua脚本实现分布式锁
local lockKey = KEYS[1]
local lockValue = ARGV[1]
local lockTimeout = tonumber(ARGV[2])

-- 尝试获取锁
local result = redis.call('SET', lockKey, lockValue, 'NX', 'EX', lockTimeout)

-- 如果获取锁成功,返回1;否则返回0
if result then
return 1
else
return 0
end

在Redis中执行该Lua脚本的命令如下:

1
EVAL "local lockKey = KEYS[1]; local lockValue = ARGV[1]; local lockTimeout = tonumber(ARGV[2]); local result = redis.call('SET', lockKey, lockValue, 'NX', 'EX', lockTimeout); if result then return 1 else return 0 end" 1 lockKey lockValue lockTimeout
  • EVAL:Redis的命令,用于执行Lua脚本。
  • "local lockKey = KEYS[1]; local lockValue = ARGV[1]; local lockTimeout = tonumber(ARGV[2]); local result = redis.call('SET', lockKey, lockValue, 'NX', 'EX', lockTimeout); if result then return 1 else return 0 end":Lua脚本内容。
  • 1:表示脚本中有一个键(lockKey)。
  • lockKeylockValuelockTimeout:传递给Lua脚本的参数。

Redis事务

除了使用Lua脚本,Redis事务也可以保证Redis的原子性。Redis事务通过MULTIEXECDISCARDWATCH命令来实现。事务可以将多个命令打包成一个整体执行,从而保证这些命令在执行过程中不会被其他命令打断,保证操作的原子性。

Redis事务的命令

  • **MULTI**:标记一个事务块的开始。
  • **EXEC**:执行事务块中的所有命令。
  • **DISCARD**:取消事务,放弃执行事务块中的所有命令。
  • **WATCH**:监视一个或多个键,如果在事务执行之前这些键被其他命令修改,事务将被中断。

Redis事务的执行流程

  1. 开启事务:使用MULTI命令开启一个事务。
  2. 添加命令:在事务块中添加多个Redis命令。
  3. 执行事务:使用EXEC命令执行事务块中的所有命令。
  4. 取消事务(可选):如果需要取消事务,可以使用DISCARD命令。

假设我们需要执行以下两条命令,并确保它们具有原子性:

  1. 将键key1的值设置为value1
  2. 将键key2的值设置为value2

我们可以使用Redis事务来实现:

1
2
3
4
5
6
7
8
9
# 开启事务
MULTI

# 添加命令
SET key1 value1
SET key2 value2

# 执行事务
EXEC

Redis事务在执行过程中,如果某个命令执行失败,Redis不会进行事务回滚。也就是说,即使某个命令执行失败,事务块中的其他命令仍然会继续执行。

日志

Redis的持久化

Redis是一个高性能的内存数据库,所有的读写操作都在内存中进行。然而,内存中的数据在系统重启后会丢失,为了确保数据在重启后能够恢复,Redis提供了持久化机制,将数据存储到磁盘中。Redis的持久化主要通过两种机制实现:AOF(Append-Only File)日志和RDB(Redis Database)快照。

Redis的持久化主要通过两种机制:

  • AOF日志:每执行一条操作命令,就把该命令以追加的方式写入日志文件中。
  • RDB快照:将某一刻的内存数据,以二进制的方式写入磁盘中。

AOF日志实现

AOF日志是一种记录Redis操作命令的持久化方式。每当Redis执行一条写操作命令时,该命令会被追加到AOF日志文件中。当Redis重启时,可以通过读取AOF日志文件并逐行执行命令来恢复数据。

AOF执行流程

Redis提供了三种AOF日志回写硬盘的策略:

  1. Always:每执行一条命令后,立即将该命令写入磁盘日志文件中。这种策略保证了数据的实时持久化,但会对性能产生较大影响。
  2. Everysec:每秒将AOF日志文件的内核缓冲区中的数据写入磁盘日志文件。这种策略在性能和数据安全性之间取得了平衡。
  3. No:将命令写入AOF日志的内核缓冲区,由操作系统决定何时将日志写入磁盘日志文件。这种策略性能最高,但数据安全性较低。

RDB快照实现

因为AOF日志记录的是命令操作,并非实际的数据。当AOF日志巨大时,Redis在启动中执行一次全量的命令操作,势必造成Redis恢复操作缓慢。为了解决这个问题,Redis增加了RDB快照。

RDB快照是一种记录Redis内存数据状态的持久化方式。RDB快照将某一时刻的内存数据以二进制格式写入磁盘文件中。当Redis重启时,可以通过加载RDB快照文件来快速恢复数据。

Redis提供了两条命令来生成RDB快照文件:

  1. save:在主线程中生成RDB快照文件。由于是在主线程中执行,如果快照文件较大,可能会阻塞主线程,影响Redis的性能。
  2. bgsave:创建一个子线程来生成RDB快照文件。这种方式避免了主线程的阻塞,提高了Redis的性能。