Redis 管道及事务

Redis 管道及事务

redis pipeline 是一个客户端提供的,而不是服务端提供的。
在这里插入图片描述
Redis 管道(Pipeline)是一种优化 Redis 批量操作的机制,可以将多个命令一次性发送到 Redis 服务器,并一次性接收它们的回复。这样可以减少客户端和服务器之间的网络往返次数,从而提高了操作的效率。
使用管道可以在以下场景中获得性能提升:
批量写入操作:当需要执行多个写入操作时,使用管道可以将它们一次性发送到服务器,减少了每个操作之间的网络往返时间。
**批量读取操作:**类似地,当需要获取多个键的值时,使用管道可以一次性发送多个命令,并一次性接收它们的回复,提高了读取操作的效率。
**原子性操作:**在管道中,多个命令被打包成一个原子操作,保证了它们的执行是连续的、不可中断的。

type Conn interface {
    // Close closes the connection.
    Close() error
    // Err returns a non-nil value when the connection is not usable.
    Err() error
    // Do sends a command to the server and returns the received reply.
    Do(commandName string, args ...interface{}) (reply interface{}, err 
error)
    // Do = Send + Flush + Receive
    // Send writes the command to the client's output buffer.
    Send(commandName string, args ...interface{}) error
    // Flush flushes the output buffer to the Redis server.
    Flush() error
    // Receive receives a single reply from the Redis server
    Receive() (reply interface{}, err error)
 }

Send(commandName string, args …interface{}) error:将命令写入客户端的输出缓冲区。参数和 Do 方法类似,用于发送命令但不立即执行。
**Flush() error:**将输出缓冲区中的内容刷新到 Redis 服务器。执行此操作后,客户端将向服务器发送所有已经排队的命令。
Receive() (reply interface{}, err error):从 Redis 服务器接收单个回复。通常与 Send 和 Flush 方法一起使用,用于接收之前发送的命令的回复。

管道使用技巧

// 创建一个管道
	c := redis.NewPipeline(conn)
// 批量发送,批量接收
c.Send(cmd1, ...)
 c.Send(cmd2, ...)
 c.Send(cmd3, ...)
 c.Flush() // 将上面的三个命令发送出去
c.Receive() // cmd1 的返回值
c.Receive() // cmd2 的返回值
c.Receive() // cmd3 的返回值
// 如果不需要关注返回值
c.Send(cmd1, ...)
 c.Send(cmd2, ...)
 c.Send(cmd3, ...)
 c.Do("")
 // 如果只关注最后一个命令的返回值
c.Send(cmd1, ...)
 c.Send(cmd2, ...)
 c.Do(cmd3, ...)

Redis 网络事件处理

在这里插入图片描述
Redis 是单线程处理逻辑的,这意味着 Redis 服务器在任意时刻只能处理一个命令,而不是像传统的多线程服务器一样并行处理多个命令。每条连接的读缓冲区相当于一个队列;线程会交错执行活跃连接的命令;
在 Redis 中,网络事件处理和命令处理都是在同一个线程中进行的。**服务器通过轮询每个连接,从连接的读缓冲区中读取数据包,并将其分派给相应的命令处理逻辑。****每条连接的读缓冲区相当于一个队列,线程会交错执行活跃连接的命令,**以确保所有连接都得到及时的处理。
由于 Redis 是单线程处理逻辑的,它具有以下优点和特点:

简单高效:单线程模型减少了线程间的竞争和同步开销,使得 Redis 在处理命令时非常高效。

避免了复杂性:单线程模型减少了并发编程的复杂性,避免了锁和同步等并发问题。

原子性操作:Redis 命令的执行是原子性的,即每个命令在执行时不会被中断,可以确保数据的一致性和可靠性。

非阻塞 I/O:由于 Redis 使用非阻塞 I/O 模型,可以处理大量的并发连接,而不会因为 I/O 操作而阻塞线程。

Redis 事务

Redis 事务使用 MULTI、EXEC、DISCARD 和 WATCH 四个命令来管理事务的执行过程:

MULTI:标记事务的开始,之后的所有命令都会被放入事务队列中,直到执行 EXEC 或 DISCARD 命令。

EXEC:执行事务中的所有命令。如果在执行期间没有出现错误,事务中的所有命令都会被执行;如果其中有任何一个命令执行出错,所有命令都会被取消。

DISCARD:取消事务,清空事务队列中的所有命令,放弃事务的执行。

WATCH:用于在事务开始前监视一个或多个键,如果在事务执行期间这些键被其他客户端修改,事务将被取消。在事务开启前调用,乐观锁实现(cas);若被取消则事务返回 。

应用

 # 实现 string 的 加倍操作
WATCH score:10001
 val = GET score:10001
 MULTI
 SET score:10001 val*2
 EXEC

lua脚本实现原子性

通过 Lua 脚本,可以将多个 Redis 命令组合成一个原子性操作,在执行过程中不会被中断,保证了数据的一致性和可靠性。
注意:如果项目中使用了 lua 脚本,不需要使用上面的事务命令;

 # 从文件中读取 lua脚本内容
cat test1.lua | redis-cli script load --pipe
 # 加载 lua脚本字符串 生成 sha1
 > script load 'local key = KEYS[1];local s = redis.call("get", 
key);redis.call("set", key, s*2);return s*2'
 "8f7d021dcc386a422e0febe38befdc6084357610"
 # 检查脚本缓存中,是否有该 sha1 散列值的lua脚本
> script exists "8f7d021dcc386a422e0febe38befdc6084357610"
 1) (integer) 1
 # 清除所有脚本缓存
> script flush
 OK
 # 如果当前脚本运行时间过长,可以通过 script kill 杀死当前运行的脚本
> script kill
 (error) NOTBUSY No scripts in execution right now.

EVAL

EVAL 是 Redis 中用于执行 Lua 脚本的命令之一。通过 EVAL 命令,可以在 Redis 服务器端执行 Lua 脚本,并获取执行结果。EVAL 命令的基本语法如下:

EVAL script numkeys key [key ...] arg [arg ...]
  • script 是要执行的 Lua 脚本;
  • numkeys 表示键的数量,即 Lua 脚本中访问的 Redis 键的数量;
  • key [key …] 是 Lua 脚本中访问的 Redis 键;
  • arg [arg …] 是传递给 Lua 脚本的参数。

EVAL 命令执行 Lua 脚本的过程如下:

  1. 如果 **Lua 脚本中访问了 Redis 的键,需要将这些键作为参数传递给 EVAL 命令。**这些键会被传递给 Lua
    脚本,并在脚本中以 KEYS[1], KEYS[2], … 的形式访问。
  2. 如果 Lua 脚本需要接收参数,这些参数也需要作为 EVAL 命令的参数传递进来。这些参数会被传递给 Lua 脚本,并在脚本中以
    ARGV[1], ARGV[2], … 的形式访问。
  3. Redis 服务器执行 Lua 脚本,并返回执行结果。

下面是一个简单的示例,演示了如何使用 EVAL 命令执行一个 Lua 脚本:

EVAL "return 'Hello, world!'" 0

在这个示例中,Lua 脚本是简单的返回了一个字符串 ‘Hello, world!’。numkeys 参数是 0,表示 Lua 脚本没有访问 Redis 键。因此命令只包含了脚本本身,没有传递任何键。执行结果会返回字符串 ‘Hello, world!’。

EVALSHA

EVALSHA 是 Redis 中用于执行 Lua 脚本的另一个命令,它与 EVAL 类似,但是 EVALSHA 使用的是已经缓存的脚本的 SHA1 散列值,而不是直接传递脚本的字符串。这样可以减少网络传输的开销,并且提高了安全性。

EVALSHA 命令的基本语法如下:

EVALSHA sha1 numkeys key [key ...] arg [arg ...]
  • sha1 是 Lua 脚本的 SHA1 散列值;
  • numkeys 表示键的数量,即 Lua 脚本中访问的 Redis 键的数量;
  • key [key …] 是 Lua 脚本中访问的 Redis 键;
  • arg [arg …] 是传递给 Lua 脚本的参数。

EVALSHA 命令执行 Lua 脚本的过程与 EVAL 类似,但是它使用脚本的 SHA1 散列值来识别和执行脚本。
使用 EVALSHA 命令的好处是,如果多个客户端同时执行相同的 Lua 脚本,服务器只需要缓存一个副本,不需要重复解析和编译脚本,从而节省了服务器的资源,并提高了执行效率。

下面是一个示例,演示了如何使用 EVALSHA 命令执行一个已经缓存的 Lua 脚本:

EVALSHA "16f99e1b4a265e29d33c68c7b2f673b1f53f82d9" 0

在这个示例中,16f99e1b4a265e29d33c68c7b2f673b1f53f82d9 是 Lua 脚本的 SHA1 散列值,表示要执行的 Lua 脚本。numkeys 参数是 0,表示 Lua 脚本没有访问 Redis 键。因此命令只包含了脚本的 SHA1 散列值,没有传递任何键。执行结果会根据 Lua 脚本的返回值而变化。

应用

 # 1: 项目启动时,建立redis连接并验证后,先加载所有项目中使用的lua脚本(script load);
 # 2: 项目中若需要热更新,通过redis-cli script flush;然后可以通过订阅发布功能通知所有服
务器重新加载lua脚本;
# 3:若项目中lua脚本发生阻塞,可通过script kill暂停当前阻塞脚本的执行;

事务 ACID 特性分析

**A 原子性;**事务是一个不可分割的工作单位,事务中的操作要么全部成功,要么全部失败;redis不支持回滚;即使事务队列中的某个命令在执行期间出现了错误,整个事务也会继续执行下去,直到将事务队列中的所有命令都执行完毕为止。
C 一致性;事务使数据库从一个一致性状态到另外一个一致性状态;这里的一致性是指预期的一致性而不是异常后的一致性;所以redis也不满足;
I 隔离性;事务的操作不被其他用户操作所打断;redis命令执行是串行的,redis事务天然具备隔离性;
D 持久性;redis只有在 aof 持久化策略的时候,并且需要在 redis.conf 中 appendfsync=always 才具备持久性;实际项目中几乎不会使用

  • 11
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值