Redis客户端使用RESP(Redis序列化协议)与Redis服务器进行通信,RESP在位于TCP之上,而网络模型上客户端和服务器是保持的双工的连接。如图1

而一个简单的请求/响应的串行通信模型如下图:

串行化通信

串行化通信比较简单,上面那张图就很表面的反应出来这种通信方式,同一个Connction需要在等上一个命令执行完成之后在执行下一个命令,我们在前面文章讲Redis各种类型的时候做的测试,就是用这种方式。客户端发送一个指令到Redis实例,Redis实例处理完成之后将结果返回给客户端。

前面文章说Redis为什么要用多线程中有说过,Redis处理请求的速度特别快,我们一个请求的瓶颈主要是在I/O上面,而对于串行化通信,每一个请求的发送都要等到上一个请求的响应介绍,因此在串行模式下,单连接的大部分时间都浪费在网络等待上,没有充分的利用服务器的处理能力

管道技术

Redis在很早的时候就支持管道技术了,简单来说,就是可以完全无需等待服务端应答地发送多条指令给服务端,并最终一次性读取所有应答。管道技术最显著的优势是提高了redis服务的性能,通过管道技术来进行大批量的操作的时候,可以节省很多在网络延迟上的时间。

在.net core 的Redis客户端StackExchange.Redis则是基于Task来实现管道技术,而StackExchangeRedis本身的异步也都是通过管道技术来实现。

事务

在菜鸟教程中是这么介绍的

Redis 事务可以一次执行多个命令, 并且带有以下三个重要的保证:

  • 批量操作在发送 EXEC 命令前被放入队列缓存。
  • 收到 EXEC 命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行。
  • 在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。

一个事务从开始到执行会经历以下三个阶段:

  • 开始事务。
  • 命令入队。
  • 执行事务
  • 放弃事务

原理很简单,客户端发送命令MULTI,服务器会将后续的命令都放入队列缓存,直到收到EXEC命令才会依次执行命令。单个Redis的命令是原子性的,但是Redis并没有在事务上增加任何的维持原子性的机制,当中间某条命令失败并不会导致其他命令的回滚,这个跟我们在关系型数据库的理解不一样,更多的像一个打包的批处理脚本。

菜鸟中有这么一句话

在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。

粗略一看我还理解为事务开启会阻塞其他客户端的命令,吓得我马上做了一下测试

在客户端1中开启事务multi,并发送一个set 和 get 的命令,能看到都是QUEUED的状态,表明是正确的入队了

接着在客户端2中获取key1发现值是null,说明客户端1的命令还没有真正执行,接着设置key1的值为value2,接着取得key1的值,在客户端1中开启事务后,在客户端2是可以顺利执行命令的,菜鸟中的话的意思其实客户端的命令不会进入开启事务那个客户端的命令队列中。

我们接着在客户端1提交命令,key1的值变为value1,客户端2中设置的value2被更改为value1了。

我们将Redis事务与数据库事务的四大特征对比下

原子性 不支持 Redis单个指令是具有原子性的,但是事务没有
一致性 不支持 在上面的例子就可以看见,在客户端1的事务开启的时候,我仍然能修改key1的值,在关系型数据库中我们有悲观锁和乐观锁来解决这种并发问题,Redis也通过Watch可以实现乐观锁的效果,但是我还是没有体会出来有什么用处。在关系型数据中的事务,我们可能会先取出来值,在进行修改,最后提交事务,如果没有锁来保证,那么我们最后的数据就没有一致性了,但是对于Redis我还是没想出来什么场景下会需要用乐观锁来控制并发,知道的小伙伴麻烦告知一声。
隔离性 支持 Redis本身是没有隔离性这个说法的,之所以我觉得是支持隔离性,因为我觉得Redis的事务都是在最后才执行,而本身命令又是原子性的,所以隔离性对Redis是无意义的。
持久性 不支持 Redis有持久化方案,但是最高数据安全性的方式-AOF中的修改同步,仍然会在异常情况下导致数据丢失。

其实这个对比不太恰当,Redis的事务只是顶着事务这个名字,做的还是批量处理的事情,它的关注点不应该在正真的事务上

脚本

在说事务的时候有说事务更像是批处理的感觉,而脚本也是批处理,不同的是,我们可以根据上一个指令的结果作为我们下个指令的参数,这是处理逻辑问题的时候特别有用。

Redis脚本是通过Eval命令实现,当客户都安使用Eval命令的时候,Redis实例会通过lua解释器来执行脚本,我们这里的脚本也是lua脚本,用Abp中清除缓存的的源码作为示例

EVAL "local keys = redis.call('keys', ARGV[1])
for i=1,#keys,5000
do
redis.call('del', unpack(keys, i, math.min(i+4999, #keys)))
end"
0 'Test_*'

这个脚本第一步将以Test做为前缀的key全部取出来存入变量keys,接着从1开始,以keys的长度为最大值,步长为5000进行遍历,每一步都是删除5000个key。为什么要用每次5000遍历来执行呢?因为unpack函数在数量太多的时候会出现 'too many results to unpack' 的错误,我们来实际操作下,往实例中添加10个用Test_为前缀的值,然后执行上面的脚本

可以看到我们以Test_做为前缀的Key都被删除了

发布/订阅模式

前面有讲到过,Redis实例和客户都之间是双工连接的,但是前面所说的不管是简单的命令还是事务脚本都是客户端主动发起请求,Redis实例被动回应的,而发布/订阅模式则是可以由Redis实例主动给客户端发送消息,在下一节会详细说这种模式。

Redis服务器和客户端的通信的更多相关文章

  1. 基于 HTML5 WebGL 的 3D 服务器与客户端的通信

    这个例子的初衷是模拟服务器与客户端的通信,我把整个需求简化变成了今天的这个例子.3D 机房方面的模拟一般都是需要鹰眼来辅助的,这样找产品以及整个空间的概括会比较明确,在这个例子中我也加了,这篇文章就算 ...

  2. Socket通信——服务器和客户端相互通信

    所谓socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄.应用程序通常通过"套接字"向网络发出请求或者应答网络请求.  Socket和S ...

  3. C#利用服务器实现客户端之间通信

    这两天在学习C#,C#高级编程真的是厚厚的一本书QAQ. 昨天看了一下里面的通信部分(其实还没怎么看),看了网上一些人的博客,自己在他们的博客基础上写了一个通信. 先来讲述下我自己对于整个Socket ...

  4. nodejs+expressjs+ws实现了websocket即时通讯,服务器和客户端互相通信

    nodejs代码 // 导入WebSocket模块: const WebSocket = require('ws'); // 引用Server类: const WebSocketServer = We ...

  5. [转]redis服务器与客户端保活参数(tcp-keepalive)设置

    最近使用redis的list做跨进程的消息队列,客户端使用的是redis-cplusplus-client.这个client库还是蛮好用的,提供了和redis命令行一致的接口,很方便. 使用过程中发现 ...

  6. ajax利用json进行服务器与客户端的通信

    1.JQuery中$.ajax()方法参数详解 http://blog.sina.com.cn/s/blog_4f925fc30100la36.html 2.服务器端获取String que=requ ...

  7. Redis源码解析:14Redis服务器与客户端间的交互

    Redis服务器是典型的一对多服务器程序,通过使用由IO多路复用技术实现的文件事件处理器,Redis服务器使用单线程单进程的方式来处理命令请求,并与多个客户端进行网络通信. Redis客户端与服务器之 ...

  8. GATT两个角色 服务器与客户端

    两个设备应用数据的通信是通过协议栈的GATT层实现的. 从GATT角度来看,当两个设备建立连接后,他们处于以下两种角色之一: GATT服务器: 它是为GATT客户端提供数据服务的设备 GATT客户端: ...

  9. GATT 服务器与客户端角色

    两个设备应用数据的通信是通过协议栈的GATT层实现的.从GATT角度来看,当两个设备建立连接后,他们处于以下两种角色之一: GATT服务器: 它是为GATT客户端提供数据服务的设备 GATT客户端:  ...

随机推荐

  1. CVE-2019-17671:wrodpress 未授权访问漏洞-复现

    0x00 WordPress简介 WordPress是一款个人博客系统,并逐步演化成一款内容管理系统软件,它是使用PHP语言和MySQL数据库开发的,用户可以在支持 PHP 和 MySQL数据库的服务 ...

  2. 解决xcode ***is missing from working copy

    这是由于SVN置顶文件导致的,cd 至项目根目录 命令行 输入 find . -type d -name .svn | xargs rm -rf

  3. 2019-07-25【机器学习】无监督学习之聚类 K-Means算法实例 (1999年中国居民消费城市分类)

    样本 北京,2959.19,730.79,749.41,513.34,467.87,1141.82,478.42,457.64天津,2459.77,495.47,697.33,302.87,284.1 ...

  4. vue2.x学习笔记(十四)

    接着前面的内容:https://www.cnblogs.com/yanggb/p/12602256.html. 组件的Prop Prop是组件之间通信的一个重要途径,了解其知识十分重要. Prop的大 ...

  5. django-admin和manage.py用法

    官网文档地址:django-admin和manage.py 金句: 所有的天赋,都来自于你对你喜欢的某种事物的模仿与学习,否则你就不会有这种天赋. 开篇话: 我们在Django开发过程中,命令行执行最 ...

  6. Jmeter系列(5)- jmeter.properties常用配置项讲解

    如果你想从头学习Jmeter,可以看看这个系列的文章哦 https://www.cnblogs.com/poloyy/category/1746599.html jmeter.properties 所 ...

  7. 2019-2020-1 20199329《Linux内核原理与分析》第十一周作业

    <Linux内核原理与分析>第十一周作业 一.本周内容概述: 学习linux安全防护方面的知识 完成实验楼上的<ShellShock 攻击实验> 二.本周学习内容: 1.学习& ...

  8. 2019-2020-1 20199329《Linux内核原理与分析》第二周作业

    <Linux内核原理与分析>第二周作业 一.上周问题总结: 未能及时整理笔记 Linux还需要多用 markdown格式不熟练 发布博客时间超过规定期限 二.本周学习内容: <庖丁解 ...

  9. MySql id 设定为主键不自增后,再给 sort 字段增加自增属性

    需求 id 已经被设置为主键,但是没有给它设置 自增 属性.sort 起到一个排序的作用,需要给它设置一个 自增 属性 加自增属性的前提 表中的属性没有增加自增 赋予自增属性的字段,必须带有 索引 S ...

  10. 如何在Spring boot中修改默认端口

    文章目录 介绍 使用Property文件 在程序中指定 使用命令行参数 值生效的顺序 如何在Spring boot中修改默认端口 介绍 Spring boot为应用程序提供了很多属性的默认值.但是有时 ...