Redis事务与异步方式

redis pipeline

redis pipeline 是一个redis-cli提供的机制,而不是redis-server提供的;

目的:节约网络传输时间;

Redis通过网络传输请求通常由同步和异步两种方式:

同步:发一条请求,等待回复,再发一条请求,等待回复……

异步:一次性发送N条请求,然后等待回复。

前者能保证发送和回复的顺序性,后者则能保证高效率地发送。

redis pipeline属于异步网络传输。

Redis的发布-订阅模式

Redis事务与异步方式_第1张图片

举例:当有三个服务器连接了一个redis,当有一条新的message进入了redis,这三个服务器都想能马上了解怎么办呢?

根据redis的监听-发布者订阅模式,需要这么操作:

  1. 建立一个channel。
  2. 三个服务器都去监听这个channel。
  3. 如果有一条新的message想迅速地被三个服务器知道,那么它往这这个channel发布message。
  4. 当然,message也能是其中一个server发布的,通过监听-发布者模式也能让三个server都迅速获message。

相关代码:

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

Redis事务与异步方式_第2张图片

(右上角是发布者,其余是监听者。)

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

Redis事务与异步方式_第3张图片

一个redis-cli可以与数据库建立多条连接。

发布订阅功能一般要给同一个redis-cli开两个连接,一个负责监听,一个负责日常发命令;因为命令连接严格遵循请求回应模式;而 pubsub 能收到 redis 主动推送的内容。

所以实际项目中如果支持 pubsub 的话,需要另开一条连接用于处理发布订阅。

注意:发布订阅的生产者传递过来一个消息,redis 会直接找到相应的消费者并传递过去;假如没有消费者,消息直接丢弃;假如开始有2个消费者,一个消费者突然挂掉了,另外一个消费者依然能收到消息,但是如果刚挂掉的消费者重新连上后,在断开连接期间的消息对于该消费者来说彻底丢失了;另外,redis 停机重启,public和 subscribe 的消息是不会持久化的,所有的消息被直接丢弃;

所以我们只能在要求不确保消息到达的时候使用pub/sub模式。如果想确保消息一定能到达,需要使用其他模式,比如kafka,stream。

Redis事务

如果redis-server几乎同时收到了来在redis-cliA的3条操作指令,收到了redis-cliB的5条操作指令。因为redis-server处理命令的时候是单线程的,一次只能执行一条命令(具备原子性),有可能把A的3条命令和B的5条命令穿插着处理。如果有要求A和B的命令不能分割处理,要么先处理完A的再处理B的,要么先处理完B的再处理A的该怎么办呢?

这时候需要把A的命令和B的命令再发送前打包成【事务】。

事务:用户定义一系列数据库操作,这些操作视为一个完整的 逻辑处理工作单元,要么全部执行,要么全部不执行,是不可分割的工作单元。如果只有一个redis-cli连接redis-server,那么不用考虑事务。

Rredis事务的操作命令(了解即可,实务几乎不用):

MULTI:开启事务

EXEC:提交事务

DISCARD:取消事务

WATCH:检测 key

逻辑图:

Redis事务与异步方式_第4张图片

如果MULTI之前先WATCH了某个key,这个key在EXEC之前发生了值变化,那么EXEC会清空事件队列。如果MULTI和EXEC中间pop了DISCARD命令,那么EXEC会清空事件队列。

然而,我们一般使用lua脚本来执行redis事务。

Redis-cli发送一串lua脚本给redis-server,然后redis-server用内置的lua虚拟机执行接收到的lua脚本。

# 测试使用
EVAL script numkeys key [key ...] arg [arg ...]
# 线上使用
EVALSHA sha1 numkeys key [key ...] arg [arg ...]

key [key ...]是一个数字,表示script里用到的redis key的数量,arg [arg ...]则表示redis key的名字。

Sha怎么产生?用SCRIPT命令把命令注册到redis-server中
该方法有两个优点:1、用短短的字符串代替复杂脚本。2、只需要编译注册一次,能节省资源。

redis事务的ACID特性分析

redis满足A与I,不满足C与D。

A原子性;redis事务是一个不可分割的工作单位,事务中的操作要么全部执行,要么事前检测出事务中有错误的、不可执行的部分全部不执行;redis事务的原子性体现在此处。但是redis 不支持回滚;如果redis事务事前没检测出错误,事务正常执行,然而事务队列中的某个命令在执行期间出现了错误(比如有可能是因为内存不足的原因),整个事务也会继续执行下去,直到将事务队列中的所有命令都执行完毕为止,这时候就会没有严格的原子性了,会出现一部分命令成功,一部分命令执行失败的情况,需要我们人工回滚。

I 隔离性;各个事务之间互不影响;redis 是单线程执行,天然具备隔离性;

C 一致性;redis 不能确保事务执行前后的数据的完整约束,也就是说数据状态(数据的取值或内容)会有事务内部操作只成功了部分,导致事务结束之后,数值的变化和事前预期不一致的情况。而且并不满足业务功能上的一致性(一致性的满足要么数据全部成功,要么出现失败然后全部回滚);比如转账功能,一个扣钱一个加钱;可能出现扣钱执行错误,加钱执行正确,那么最终还是会加钱成功;系统凭空多了钱;

 D持久性;redis 只有在 aof 持久化策略的时候,并且需要在 redis.conf 中appendfsync=always 才具备持久性;实际项 目中几乎不会使用 aof 持久化策略;

Redis异步连接

同步连接:等待redis返回了命令执行后的报文再继续发送下一条命令。

异步连接:不等待redis返回命令执行后的报文,照样继续发送后续的报文。

异步连接的速度远高于同步连接的速度,甚至达到了数十倍,缺点是可能会造成业务逻辑的割裂。

如何实现异步连接了?
1、另起线程。

2、使用reactor模型。可以使用开源框架,hiredis(hi,redis)制作客户端。

3、使用proactor模型。

本文介绍一下使用基于hiredis建立能与redis通信reactor服务器的逻辑。

技术拆解:

1、与redis建立连接

        a、创建socket,设置fd为非阻塞。

        b、connet。

        c、fd注册到epoll当中,绑定写事件。

        d、如果连接建立成功,写事件会进行响应,然后注销写事件,注册读事件。

2、向redis发送数据

        a、int n = write(fd,buf,size),根据redis协议加密数据包;如果n

        b、注册写事件,如果写事件触发,继续发送数据,如果全部发送,注销写事件。通常来说客户端不需要通过epoll发现写事件,就能直接发送数据。但是异步发送数据速度非常快,可能会因为缓冲区的限制造成无法发送数据,这时候需要用epoll检测写事件的触发然后再行发送。

        c、注册读事件。

3、读取redis 的返回

        a、读事件触发

        b、根据redis协议分割数据包

其中最关键的是,我们大多是时候不希望自己解析redis协议,那么就需要需要做的是把hiredis的适配器的关键接口实现。

若需要相关的demo可私信联系。

你可能感兴趣的:(redis,数据库,缓存,分布式,服务器,c语言,objective-c)