当前位置:网站首页>.net core 关于redis的pipeline以及事务
.net core 关于redis的pipeline以及事务
2022-07-07 11:36:00 【小兜全糖(Cx)】
Redis本身是基于Request/Response协议(停等机制)的,正常情况下,客户端发送一个命令,等待Redis返回结果,Redis接收到命令,处理后响应。在这种情况下,如果同时需要执行大量的命令,那就是等待上一条命令应答后再执行,这中间不仅仅多了RTT(Round Time Trip),而且还频繁调用系统IO,发送网络请求。为了提升效率,这时候pipeline出现了,它允许客户端可以一次发送多条命令,而不等待上一条命令执行的结果,这和网络的Nagel算法有点像(TCP_NODELAY选项)。pipeline不仅减少了RTT,同时也减少了IO调用次数(IO调用涉及到用户态到内核态之间的切换)。
要支持Pipeline,其实既要服务端的支持,也要客户端支持。对于服务端来说,所需要的是能够处理一个客户端通过同一个TCP连接发来的多个命令,可以理解为,这里将多个命令切分,和处理单个命令一样(之前老生常谈的黏包现象),Redis就是这样处理的。而客户端,则是要将多个命令缓存起来,缓冲区满了或者达到发送条件就发送出去,最后才处理Redis的应答。
简单描述其实就是一次性发送多个redis命令道服务器,减少了请求次数

Redis的Pipeline和Transaction(Redis事务)不同,Transaction会存储客户端的命令,最后一次性执行,而Pipeline则是处理一条(批次),响应一条,从二者的不同处理机制来看,Redis事务中命令的执行是原子的(注意,其中一部分命令出现错误后续命令会继续执行,这里的原子说的是命令执行是完整的,中间不会被其他Redis命令所打断),而pipeline中命令的执行不一定是原子的。但是这里却有一点不同,就是pipeline机制中,客户端并不会调用read去读取socket里面的缓冲数据(除非已经发完pipeline中所有命令),这也就造成了,如果Redis应答的数据填满了该接收缓冲(SO_RECVBUF),那么客户端会通过ACK,WIN=0(接收窗口)来控制服务端不能再发送数据,那样子,数据就会缓冲在Redis的客户端应答缓冲区里面。所以需要注意控制Pipeline的大小
Batch批量操作
StackExchange.Redis中对于连续多次的缓存等请求,我们会多次调用相关的函数来执行Redis命令。然而这种方式有个弊端就是每一次的请求都需要等待返回结果
如果在网络状况不好的情况下,可能会造成不好的用户体验。
对于这种问题可以用StackExchange.Redis提供的CreateBatch()解决
public void TestPipeLine()
{
IDatabase db = StackExchangeRedisHelper.GetDatabase();
var batch = db.CreateBatch();
Task t1 = batch.StringSetAsync("name", "bob");
Task t2 = batch.StringSetAsync("age", 100);
batch.Execute();
Task.WaitAll(t1, t2);
Console.WriteLine("Age:" + db.StringGet("age"));
Console.WriteLine("Name:" + db.StringGet("name"));
}
redis 的事务有ACID四个特性
redis的事务实际上是运行了multi 命令,然后运行想要运行的命令,最后执行exec命令做一次提交就可以
StackExchange.Redis中的事物控制
multi命令只是保证一个事物中的所有命令可以在一起执行,显然只是实现这一点的话对于大部分的业务都是无法满足的。
所以Redis提供了Watch命令来监控一个key以达到乐观锁的效果。
在StackExchange.Redis是无法用watch multi命令来执行的,因为在并发环境下,会产生多个watch multi命令,全混在一起就乱套了。
但是StackExchange.Redis提供了一套非常简单易懂的创建事物的方式 ,下面为示例代码
public void TestTran()
{
IDatabase db = StackExchangeRedisHelper.GetDatabase();
string name = db.StringGet("name");
string age = db.StringGet("age");
Console.WriteLine("NAME:" + name);
Console.WriteLine("Age:" + age);
var tran = db.CreateTransaction();
tran.AddCondition(Condition.StringEqual("name", name));
Console.WriteLine("tran begin");
tran.StringSetAsync("name", "leap");
tran.StringSetAsync("age", 12);
Thread.Sleep(4000);
bool result = tran.Execute();
Console.WriteLine("执行结果:" + result);
Console.WriteLine("Age:" + db.StringGet("age"));
Console.WriteLine("Name:" + db.StringGet("name"));
}
.net core 中的使用
using CoreRedis.Config;
using Microsoft.Extensions.Options;
using StackExchange.Redis;
namespace CoreRedis.RedisDataType
{
public class RedisPipelineTranc
{
private ConnectionMultiplexer connectionMultiplexer;
private readonly RedisConfig _redisConfig;
private IDatabase db;
public RedisPipelineTranc(IOptionsMonitor<RedisConfig> optionsMonitor)
{
_redisConfig = optionsMonitor.CurrentValue;
connectionMultiplexer = ConnectionMultiplexer.Connect(_redisConfig.Value);
db = connectionMultiplexer.GetDatabase();
}
public async Task<string> testPipelien()
{
// intialize key with empty string
await db.StringSetAsync("key", "");
// create transaction that utilize multiplexing and pipelining
ITransaction transacton = db.CreateTransaction();
Task<long> appendA = transacton.StringAppendAsync("key", "a");
Task<long> appendB = transacton.StringAppendAsync("key", "b");
Task<long> appendc = transacton.StringAppendAsync("key", "c");
if (await transacton.ExecuteAsync()) // sends "MULTI APPEND KEY a APPEND KEY b EXEC
// in single request to redis server
{
order here doesn't matter, result is always - "abc".
'a' and 'b' append always together in isolation of other Redis commands
'c' appends to "ab" because transaction is already executed successfully
//await appendA;
//await db.StringAppendAsync("key", "c");
//await appendB;
Task.WaitAll(appendA, appendB, appendc);
}
string value = db.StringGet("key"); // value is "abc"
return value;
}
}
}
边栏推荐
- 线程池拒绝策略最佳实践
- Getting started with MySQL
- 1、深拷贝 2、call apply bind 3、for of for in 区别
- [1] Basic knowledge of ros2 - summary version of operation commands
- Cinnamon 任务栏网速
- Scripy tutorial classic practice [New Concept English]
- 【黑马早报】华为辟谣“军师”陈春花;恒驰5预售价17.9万元;周杰伦新专辑MV 3小时播放量破亿;法华寺回应万元月薪招人...
- MATLAB中polarscatter函数使用
- LeetCode_二分搜索_中等_153.寻找旋转排序数组中的最小值
- Write it down once Net a new energy system thread surge analysis
猜你喜欢

Ways to improve the performance of raspberry pie

MySQL error 28 and solution

干货|总结那些漏洞工具的联动使用

【堡垒机】云堡垒机和普通堡垒机的区别是什么?

MATLAB中polarscatter函数使用

最佳实践 | 用腾讯云AI意愿核身为电话合规保驾护航

PAcP learning note 1: programming with pcap

提升树莓派性能的方法

xshell连接服务器把密钥登陆改为密码登陆

Write it down once Net a new energy system thread surge analysis
随机推荐
1、深拷贝 2、call apply bind 3、for of for in 区别
Getting started with cinnamon applet
Navicat运行sql文件导入数据不全或导入失败
MySQL入门尝鲜
Ogre introduction
How far can it go to adopt a cow by selling the concept to the market?
QQ medicine, Tencent ticket
[Presto profile series] timeline use
工具箱之 IKVM.NET 项目新进展
JS function returns multiple values
华为镜像地址
Use of polarscatter function in MATLAB
MongoDB的用户管理总结
记一次 .NET 某新能源系统 线程疯涨 分析
centso7 openssl 报错Verify return code: 20 (unable to get local issuer certificate)
JS determines whether an object is empty
【面试高频题】难度 2.5/5,简单结合 DFS 的 Trie 模板级运用题
记一次 .NET 某新能源系统 线程疯涨 分析
User management summary of mongodb
Summary of import, export, backup and recovery of mongodb