redis协议与异步方式


一、redis网络层

只需要一个单线程进行,一个reactor管理所有连接,每一条连接可以看作是一个队列,队列当中的元素看作一个协议包,reactor可以看作一个处理器,这是一种并发的机制。那么我们思考一下,我们每条连接,或者说不同连接当中的队列执行最后是按照什么顺序去执行的。对于一条连接来说肯定是按照顺序的。那么对于整体而言的顺序呢,这是不定的顺序。如果是多个命令不受其他事务的影响,就会把一个连接封装成一个事务。比如说我们可以把第一条连接的队列元素封装成一个事务在这里插入图片描述
对于所有连接的数据处理,redis 并发执行的;对于单条连接的数据处理,redis 串行执行的;
队列与处理器
在这里插入图片描述
并发 : 活跃队列的个数大于处理器的个数;

二、redis pipeline

http也支持pipe这种客户端的行为
redis pipeline 是一个客户端提供的,而不是服务端提供的;
pipeline不具备事务性也不具备原子性

下图的方式也会改变网络传输的时间在这里插入图片描述
对于request操作,只是将数据写到fd对应的写缓冲区,时间非常快,真正耗时操作在读取response;
我们redis事务如何与pipeline使用呢
我们来看看以下脚本的实现,这三条命令串行执行
在这里插入图片描述

三、redis事务multi,exec,lua脚本

redis事务

MULTI 开启事务,事务执行过程中,单个命令是入队列操作,直到调用 EXEC 才会一起执行;
MULTI
开启事务
在这里插入图片描述
我们来看看,我们开两个客户端对redis事务进行操作,这里模拟两个命令入队的操作,然后另一个客户端修改这个key为mark,最后提交,我们看到最后返回的结果,还是lns没有被修改,为啥会出现这种情况呢
在这里插入图片描述

begin / start transaction
EXEC
提交事务
commit
DISCARD
取消事务
rollback
WATCH检测在事务中key是否被变动,若在事务执行中,在另一个客户端key 变动则取消事务,并不会执行队列中的命令;在事务开启前调用,乐观锁实现(cas);
若被取消则事务返回 nil ;
在这里插入图片描述这些事务在实用中是不经常用到的
应用
事务实现zpop
WATCH zset
element = ZRANGE zset 0 0
MULTI
ZREM zset element
EXEC
也就是实现查找出最小值,然后pop出来,要用这些命令来实现这一事务,具备原子性
事务实现 加倍操作
WATCH score:10001
val = GET score:10001
MULTI
SET score:10001 val*2
EXEC
这些老的事务经常和pipeline一起使用
我们其实都可以用lua脚本

lua脚本

lua 脚本实现原子性,减少网络传输;
pipeline的效果 lua多个语句
redis中加载了一个 lua 虚拟机;用来执行 redis lua 脚本;redis lua 脚本的执行是原子性的;当某个脚本正在执行的时候,不会有其他命令或者脚本被执行;可以在里边做一些逻辑运算
lua 脚本当中的命令会直接修改数据状态;
注意:如果项目中使用了 lua 脚本,不需要使用上面的事务命令;
通过一个命令来调用lua脚本
我们来看看server与redis-server的交互
1、调用script load xx.lua
2、通过hash操作生成40位的字符串,可以减少网络的传输
在这里插入图片描述

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

EVAL
#测试使用
EVAL script numkeys key [key …] arg [arg …]

EVALSHA
#线上使用
EVALSHA sha1 numkeys key [key …] arg [arg …]
应用
#1: 项目启动时,建立redis连接并验证后,先加载所有项目中使用的lua脚本(script load);
#2: 项目中若需要热更新,通过redis-cli script flush;然后可以通过订阅发布功能通知所有服 务器重新加载lua脚本;
#3:若项目中lua脚本发生阻塞,可通过script kill暂停当前阻塞脚本的执行;
我们来看一下lua脚本的逻辑,redis.call(“get”,“name”)这里的name是zero voice的话,就把它设置我们传过来的参数args,并且返回一个success,否则返回失败
在这里插入图片描述
我们来运行一下,我们会拿res这个hash值作为evalsha的参数,并且告诉有几个参数,值是什么
先把脚本传到服务器当中,生成我们的hash值,根据hash值去执行这个lua脚本命令。
在这里插入图片描述
lua脚本与mysql存储的区别
mysql存储过程不具备事务性,也不具备原子性
lua脚本具备原子性的原因是,lua脚本是个命令,redis是个单线程,lua脚本是单独执行的

redis与ACID特性分析

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

四、异步连接实现

协议实现的第一步需要知道如何界定数据包
长度+二进制流
二进制流+特殊分隔符
在这里插入图片描述

hiredis是实现redis与服务器连接的协议实现,实现一个融合当前网络层的异步连接驱动
在这里插入图片描述
适配io检测
在这里插入图片描述
同步机制下执行完1000次redis命令所用的时间,采用的是,发送一次以后,必须等返回才能执行下一次发送
在这里插入图片描述
异步机制下执行完1000次redis命令所用的时间,有发送就会接收,不受是否接收的影响性能差异很大
在游戏业务当中,通常不需要连接池,只需要异步连接就行了,例如skynet异步连接
在这里插入图片描述
reactor需要重点关注的接口,re表示事件对象还有一些具体的读写事件,以及清除事件
在这里插入图片描述
封装epoll_ctrl,添加删除修改事件,比较接口与参数
在这里插入图片描述

事件对象的封装,并且比较参数
在这里插入图片描述
为什么我们有对应红黑树的状态呢,由接口决定的,因为我们有添加删除和更新,这里就有ctx这个上下文去记录。
在这里插入图片描述
那么我们思考一下,为啥封装的时候用的是event_t e这种对象的方式,在调用的时候用的是指针呢,事件触发的时候是在自己的这一层触发的,拿到的是event_t的指针要去调用redis_event_t,这个子的要有父的指针,如果封装的时候用的对象,我们是否可以靠e的指针找到我们的redis_event_t的指针呢,我们来画一下内存分布,我们调用的时候拿到的是e的指针,我们可以根据这个指针拿到ctx,我们可以直接靠强转拿到其他的指针。
在这里插入图片描述
如果我们把e的位置调整一下呢,我们还可以靠偏移去做到,用偏移函数去获取其他成员的指针,我们拿这个封装来举例子看看,这里也用的对象封装在这里插入图片描述
用offsetof反向找到首地址
在这里插入图片描述
我们再回到这里,我们要靠封装我们要靠,privdata把三个参数通过redis_event_t这个结构体全部封装完
在这里插入图片描述

接着我们来实现接口
在这里插入图片描述把privdata转化为事件对象re,再调用epoll_ctl在这里插入图片描述
我们来看看更新操作的epoll_ctrl,mask一定要对应红黑树的状态
在这里插入图片描述
我们来看看整体步骤咋完成的,通过建立连接先生成异步的上下文,设置连接成功和失败的回调,然后执行redis命令,然后事件循环,释放R事件
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Redis是一款开源的内存键值数据库,它支持多种数据结构,如字符串、哈希、列表、集合和有序集合等。Redis6是Redis的最新版本,它在之前版本的基础上增加了许多新特性和功能。下面是Redis6的概述和详细介绍。 概述: 1. 支持多线程:Redis6引入了多线程支持,可以显著提高Redis的性能,特别是在多核CPU上。 2. 支持异步IO:Redis6支持异步IO,这意味着Redis可以同时处理多个客户端请求,并且不必等待每个请求完成。 3. 支持协程:Redis6支持协程,这意味着Redis可以更好地利用CPU和内存,以提高性能和吞吐量。 4. 改进了集群模式:Redis6改进了集群模式,增加了更多的功能和可扩展性,可以更好地应对大规模应用。 5. 支持TLS:Redis6支持TLS,这意味着Redis可以更好地保护数据的安全性和隐私性。 详细介绍: 1. 多线程支持:Redis6引入了多线程支持,大大提高了Redis的性能,特别是在多核CPU上。Redis6使用了一种叫做“多线程I/O复用”的技术,可以将多个客户端请求并行处理,从而提高Redis的吞吐量。 2. 异步IO支持:Redis6支持异步IO,这意味着Redis可以同时处理多个客户端请求,并且不必等待每个请求完成。这种机制可以提高Redis的性能和吞吐量,特别是在高并发场景下。 3. 协程支持:Redis6支持协程,这意味着Redis可以更好地利用CPU和内存,以提高性能和吞吐量。协程是一种轻量级的线程,可以在不同的任务之间快速切换,从而提高系统的并发能力和效率。 4. 集群模式改进:Redis6改进了集群模式,增加了更多的功能和可扩展性,可以更好地应对大规模应用。Redis6的集群模式支持动态扩容和缩容,可以自动发现和管理节点,从而提高集群的可用性和可靠性。 5. TLS支持:Redis6支持TLS,这意味着Redis可以更好地保护数据的安全性和隐私性。TLS是一种安全传输协议,可以保护数据在传输过程中的机密性和完整性,从而避免数据被篡改或泄露。Redis6的TLS支持可以为企业和用户提供更高的安全保障。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值