当前位置:网站首页>.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;
}
}
}
边栏推荐
- JS function returns multiple values
- MATLAB中polarscatter函数使用
- LIS longest ascending subsequence problem (dynamic programming, greed + dichotomy)
- 记一次 .NET 某新能源系统 线程疯涨 分析
- 【堡垒机】云堡垒机和普通堡垒机的区别是什么?
- Flink | multi stream conversion
- 一文读懂数仓中的pg_stat
- 一文读懂数仓中的pg_stat
- 648. 单词替换 : 字典树的经典运用
- centso7 openssl 报错Verify return code: 20 (unable to get local issuer certificate)
猜你喜欢
[dark horse morning post] Huawei refutes rumors about "military master" Chen Chunhua; Hengchi 5 has a pre-sale price of 179000 yuan; Jay Chou's new album MV has played more than 100 million in 3 hours
室內ROS機器人導航調試記錄(膨脹半徑的選取經驗)
QQ medicine, Tencent ticket
Ways to improve the performance of raspberry pie
数字ic设计——SPI
Use of polarscatter function in MATLAB
Centso7 OpenSSL error Verify return code: 20 (unable to get local issuer certificate)
High end for 8 years, how is Yadi now?
Indoor ROS robot navigation commissioning record (experience in selecting expansion radius)
centso7 openssl 报错Verify return code: 20 (unable to get local issuer certificate)
随机推荐
C语言数组相关问题深度理解
Storage principle inside mongodb
Problems that cannot be accessed in MySQL LAN
ESP32构解工程添加组件
Thread pool reject policy best practices
Write it down once Net a new energy system thread surge analysis
解决缓存击穿问题
交付效率提升52倍,运营效率提升10倍,看《金融云原生技术实践案例汇编》(附下载)
My "troublesome" subordinates after 00: not bad for money, against leaders, and resist overtime
Japanese government and enterprise employees got drunk and lost 460000 information USB flash drives. They publicly apologized and disclosed password rules
Vscode编辑器ESP32头文件波浪线不跳转彻底解决
Pay close attention to the work of safety production and make every effort to ensure the safety of people's lives and property
LIS 最长上升子序列问题(动态规划、贪心+二分)
How to make the new window opened by electorn on the window taskbar
[learning notes] segment tree selection
如何让join跑得更快?
Fast development board pinctrl and GPIO subsystem experiment for itop-imx6ull - modify the device tree file
Why can basic data types call methods in JS
Distributed transaction solution
自定义线程池拒绝策略