Redis协议与异步方式

<Redis协议与异步方式>

一 Redis网络层

Redis是一个单线程reactor模型,它的主要逻辑处理是单线程的,但是io操作能够做到多线程处理。所以它可以同时有多个连接。多个连接并发执行。Redis的所有命令都是原子性的。

二 Redis事务

redis开启事务后,会把接下来的所有命令缓存到一个单独的队列中,在提交事务时,使这些命令不可被分割的一起执行完成。

如果使用了watch命令监视某一个key,如果在开启事务之后,提交事务之前,有其他连接修改了这个key,那么这个事务在被提交的时候是无法执行的,会返回nil。

以下是redis与事务相关的命令:

#MULTI 开启事务,事务执行过程中,单个命令是入队列操作,直到调用 EXEC 才会一起执行;

MULTI
EXEC #提交事务
DISCARD #取消事务
WATCH #检测 key 的变动,若在事务执行中,key 变动则取消事务;在事务开启前调用,乐观锁实现
      #(cas);若被取消则事务返回 nil ;

应用:
事务实现 zpop

WATCH zset
element = ZRANGE zset 0 0
MULTI
ZREM zset element
EXEC

事务是实现 加倍操作

WATCH score:10001
val = GET score:10001
MULTI
SET score:10001 val*2
EXEC

这种使用事务的方式,会导致事务会被频繁的取消,这不是完整的事务。通过使用lua脚本,可以解决这个问题。

lua脚本
lua 脚本实现原子性;redis中加载了一个 lua 虚拟机;用来执行 redis lua 脚本;redis lua 脚本的执行是原子性的;当某个脚本正在执行的时候,不会有其他命令或者脚本被执行;lua 脚本当中的命令会直接修改数据状态;
注意:如果项目中使用了 lua 脚本,不需要使用上面的事务命令;

三 Redis Pipeline

redis pipeline 是一个客户端提供的,而不是服务端提供的。
正常情况下,如果没有使用redis pipeline,请求和响应的交互方式如下所示:
在这里插入图片描述
此时,连续发送两条命令的执行方式是,第一条命令发送后,会等待响应。接收到响应后,才会发送第二条响应。

当使用了redis pipeline后,两条连续的命令的执行方式就是这样的:
在这里插入图片描述
从原理上说,对于request操作,只是将数据写到fd对应的写缓冲区,时间非常快,真正耗时操作在读取response。pipeline不具备事务性。

四 Redis事务的ACID特性分析

  1. A 原子性;事务是一个不可分割的工作单位,事务中的操作要么全部成功,要么全部失败;redis
    不支持回滚;即使事务队列中的某个命令在执行期间出现了错误,整个事务也会继续执行下去,直
    到将事务队列中的所有命令都执行完毕为止。
  2. C 一致性;事务使数据库从一个一致性状态到另外一个一致性状态;这里的一致性是指预期的一
    致性而不是异常后的一致性
    ;所以redis也不满足;这个争议很大:redis 能确保事务执行前后的数
    据的完整约束;但是并不满足传统意义上的一致性;比如转账功能,一个扣钱一个加钱;可能出现
    扣钱执行错误,加钱执行正确,那么最终还是会加钱成功;系统凭空多了钱;
  3. I 隔离性;事务的操作不被其他用户操作所打断;redis 是单线程执行,天然具备隔离性;
  4. D 持久性;redis只有在 aof 持久化策略的时候,并且需要在 redis.conf 中appendfsync=always 才具备持久性;实际项目中几乎不会使用 aof 持久化策略;

五 Redis发布订阅

为了支持消息的多播机制,redis 引入了发布订阅模块。关键命令如下:

# 订阅频道
subscribe 频道
# 订阅模式频道
psubscribe 频道
# 取消订阅频道
unsubscribe 频道
# 取消订阅模式频道
punsubscribe 频道
# 发布具体频道或模式频道的内容
publish 频道 内容
# 客户端收到具体频道内容
message 具体频道 内容
# 客户端收到模式频道内容
pmessage 模式频道 具体频道 内容

发布订阅功能一般要区别命令连接而重新开启一个连接。因为命令连接严格遵循请求回应模式;而
pubsub能收到redis主动推送的内容;所以实际项目中如果支持pubsub的话,需要另开一条连接
用于处理发布订阅;
在这里插入图片描述
缺点
发布订阅的生产者传递过来一个消息,redis会直接找到相应的消费者并传递过去;假如没有消费
者,消息直接丢弃;假如开始有2个消费者,一个消费者突然挂掉了,另外一个消费者依然能收到
消息,但是如果刚挂掉的消费者重新连上后,在断开连接期间的消息对于该消费者来说彻底丢失
了。stream的方式,确保消息一定可达。
另外,redis停机重启,pubsub的消息是不会持久化的,所有的消息被直接丢弃;

应用:

#第一个终端
subscribe news.
#第二个终端
publish news 'king kiss darren'

六 异步连接Redis

redis协议图
在这里插入图片描述
协议实现的第一步需要知道如何界定数据包。常用界定数据包有两种方式:

  1. 长度 + 二进制流
  2. 二进制流 + 特殊分隔符

redis混合了这两种界定数据包的方式。server返回数据的时候,返回的数据头是*n\r\n开头,这个n就表明了返回的数据长度是多少。

异步连接
同步连接方案采用阻塞io来实现;优点是代码书写是同步的,业务逻辑没有割裂;缺点是阻塞当前
线程,直至redis返回结果。
通常用多个线程来实现线程池来解决效率问题。异步连接方案采用非阻塞io来实现,优点是没有阻塞当前线程,redis 没有返回,依然可以往redis发送命令;缺点是代码书写是异步的(回调函数),业务逻辑割裂,可以通过协程解决(openresty,skynet);配合redis6.0以后的io多线程(前提是有大量并发请求),异步连接池,能更好解决应用层的数据访问性能。

redis6.0 io多线程
redis6.0版本后添加的 io多线程主要解决redis协议的压缩以及解压缩的耗时问题,一般项目中不
需要开启。如果有大量并发请求,且返回数据包一般比较大的场景才有它的用武之地。

原理:

int n = read(fd, buff, size);// redis io-threads
msg = decode(buff, size);
data = do_command(msg);
bin = encode(data, sz);// io-threads
send(fd, bin, sz1);

第一行代码表示读取发送给redis的命令,第二行代码表示解析命令,第三行代码表示redis处理命令的逻辑,第四行代码表示对redis返回结果的加密,第五行代码表示将加密好的结果返回给客户端。

可以将第一二四五行代码逻辑交给io线程池处理,以提高效率。

在配置文件中开启的方法是:

# 在 redis.conf 中
# if you have a four cores boxes, try to use 2 or 3 I/O threads, if you have a 8 cores, try to use 6 threads.
io-threads 4
# 默认只开启 encode 也就是redis发送给客户端的协议压缩工作;也可开启io-threads-do-reads yes来实现 decode;
# 一般发送给redis的命令数据包都比较少,所以不需要开启 decode 功能;
# io-threads-do-reads no
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值