redis的网络模型

redisson pull 事务 redis pipeline 事务 区别_服务器


redis使用的是单reactor网络模型,因为redis都是网络io,单reactor可以满足。

为什么mysql每一条连接对应一个线程?

mysql有网络io,磁盘io,涉及的操作比较多,需要有一个单独的线程来处理。使用单线程或者线程池都不太合适。

redis pipeline

redis pipeline 是由客户端提供的,而不是服务端提供的,是将多条命令一起发送到redis。redis是单线程的,所以会顺序执行pipeline中的命令,但是pipeline不具备事务性,所以并不会保证这些命令会一起执行。

pipeline一般和事务一起使用,让pipeline中的多个命令一起执行,不会被其他命令打断。

redisson pull 事务 redis pipeline 事务 区别_数据库_02

redis事务

MULTI EXEC事务

redis命令事务与pipeline一起使用,做一些简单的工作,让几个命令一起执行。

MULTI

开启事务

redisson pull 事务 redis pipeline 事务 区别_redis_03

queued, 表示加入一个队列,并没有执行。

EXEC

提交事务

DISCARD

取消事务

WATCH

事务开始前使用watch监测key的变动,如果事务过程中监测到key有变化,则事务提交的时候会先检测key是否有变动,如果有变动则返回nil,并不会执行事务中的命令。

redisson pull 事务 redis pipeline 事务 区别_服务器_04

lua脚本

lua脚本解决什么问题?

lua脚本主要是来实现原子性。

redis中加载了一个 lua 虚拟机;用来执行 redis lua 脚本;redis lua 脚本的执行是原子性的;当某个脚本正在执行的时候,不会有其他命令或者脚本被执行。有pipeline的效果,lua中可以写多个语句。

redis lua与mysql存储过程类似。

redis lua脚本具有原子性;mysql存储过程不具备原子性。

mysql存储过程可以写很多sql语句,减少网络传输,可以实现简单的逻辑运算;

lua脚本具备原子性,因为执行lua脚本是一个命令,redis是一个单线程,所以具备原子性,可以通过lua实现逻辑进行回滚。

lua脚本解决了哪些问题?

  1. 减少网络传输;
  2. 实现原子性。

lua脚本使用

  1. 使用script load将lua脚本传到redis
  2. evalsha执行lua脚本

redisson pull 事务 redis pipeline 事务 区别_缓存_05

ACID分析

原子性

原子性要保证要么全部成功,要么全部失败。

redis事务不支持原子性

在EXEC前,只是入队,并不做检查命令是否能执行,EXEC后,如果有命令执行失败,后面的命令仍然会执行。

redisson pull 事务 redis pipeline 事务 区别_缓存_06

使用lua脚本,可以通过逻辑判断,自己写回滚保证原子性。

一致性

一致性指不破坏完整性约束。redis事务也不满足严格意义的一致性。

隔离性

事务的操作不被其他用户操作所打断;redis 是单线程执行,天然具备隔离性。

持久性

redis支持4种持久化方式。aof、rdb、aof复写、aof-rdb混用。

只有aof方式是在单线程中进行磁盘io;其他三种是采用fork进程的方式进行持久化。

redis pub/sub

为了支持消息的多播机制,redis 引入了发布订阅模块。类似一个分布式消息队列。消息不一定可达,如果连接断开了,对于该连接来说,pub的消息就丢失了。

redis异步连接

同步连接方案采用阻塞io来实现。优点是代码书写是同步的,业务逻辑没有割裂;缺点是阻塞当前线程,直至redis返回结果。通常用多个连接实现连接池来解决效率问题。

异步连接方案采用非阻塞io来实现;优点是没有阻塞当前线程,redis 没有返回,依然可以往redis发送命令;缺点是代码书写是异步的(回调函数),业务逻辑割裂,可以通过协程解决(openresty,skynet)。

如何实现异步连接?

hiredis提供了async的方法,但只是读写的操作,实现了redis协议。如果要实现完整的异步连接,需要在hiredis的基础上,结合我们自己实现的网络库(reactor), 一起实现异步连接。

reactor主要实现io检测,当检测到有io可读可写的时候,调用hiredis的异步读写方法。需要实现几个回调函数:addRead,delRead,addWrite ,delWrite等,这几个方法主要是用来检测io的, 通过epoll_ctl设置需要检测的io事件。

hiredis里面提供了一些常用的网络库(如libevent),实现redis异步连接的example。可以参考它们,实现我们自己的redis异步连接。

redisson pull 事务 redis pipeline 事务 区别_服务器_07

  1. read、write注册时的listen事件,还是读写io?
  2. 异步请求的reactor中的event,都是对应同一个fd,这个OK吗?

同时发出多个request,操作的其实是reactor同一个event,并且会把这个fd的设置为检测读事件。等到读事件发生,怎么找到response对应的是哪个request呢?因为这是个单线程,request肯定是有顺序的,redis对于同一个连接的命令处理也是顺序的,所以按顺序就能够找到是对应的是哪个命令。

主要的工作就是对hiredis进行适配,用我们自己reactor的io检测方法适配hiredis的方法,需要比较方法,比较参数。

redisson pull 事务 redis pipeline 事务 区别_数据库_08

比较巧妙地使用struct中成员的地址,得到struct地址的方法:

redisson pull 事务 redis pipeline 事务 区别_redis_09

redisson pull 事务 redis pipeline 事务 区别_redis_10

-1642752392324)]

比较巧妙地使用struct中成员的地址,得到struct地址的方法:

[外链图片转存中…(img-Om2YyUmf-1642752392328)]

[外链图片转存中…(img-mnVv2hhk-1642752392329)]

redisson pull 事务 redis pipeline 事务 区别_数据库_11