当前位置:网站首页>.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;
}
}
}
边栏推荐
- Some principles of mongodb optimization
- MATLAB中polarscatter函数使用
- 聊聊伪共享
- Pcap learning notes II: pcap4j source code Notes
- DID登陆-MetaMask
- Introduce six open source protocols in detail (instructions for programmers)
- Getting started with MySQL
- Move base parameter analysis and experience summary
- Ikvm of toolbox Net project new progress
- 室内ROS机器人导航调试记录(膨胀半径的选取经验)
猜你喜欢
《厌女:日本的女性嫌恶》摘录
记一次 .NET 某新能源系统 线程疯涨 分析
MySQL入门尝鲜
Scripy tutorial classic practice [New Concept English]
LIS longest ascending subsequence problem (dynamic programming, greed + dichotomy)
Realize the IP address home display function and number home query
【堡垒机】云堡垒机和普通堡垒机的区别是什么?
Milkdown 控件图标
迅为iTOP-IMX6ULL开发板Pinctrl和GPIO子系统实验-修改设备树文件
C语言数组相关问题深度理解
随机推荐
[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
Final review notes of single chip microcomputer principle
ROS机器人更换新雷达需要重新配置哪些参数
Navicat run SQL file import data incomplete or import failed
自定义线程池拒绝策略
shell 批量文件名(不含扩展名)小写改大写
交付效率提升52倍,运营效率提升10倍,看《金融云原生技术实践案例汇编》(附下载)
1. Deep copy 2. Call apply bind 3. For of in differences
干货|总结那些漏洞工具的联动使用
ESP32 ① 编译环境
PAcP learning note 1: programming with pcap
Pay close attention to the work of safety production and make every effort to ensure the safety of people's lives and property
php——laravel缓存cache
C语言数组相关问题深度理解
Data refresh of recyclerview
About how appium closes apps (resolved)
Esp32 series column
Solve the cache breakdown problem
Esp32 ① compilation environment
作战图鉴:12大场景详述容器安全建设要求