背景
由于对redis缓存中数据有批量操作,例如预热缓存数据,或者在列表页批量去获取缓存数据,在使用了multi批量提交事务后,发现redis压力高居不下,而使用了pipeline之后压力回落了平常,也因为这个案例,特在此写个分析与笔记。
multi
简介
标记一个事务块的开始。 事务块内的多条命令会按照先后顺序被放进一个队列当中,最后由 EXEC 命令原子性(atomic)地执行。
实现原理
我用php扩展调起redis服务,执行,代码如下:
$redis = new redis();
$redis->connect('127.0.0.1',6379);
$handle = $redis->multi();
$handle->incr('a');
$handle->incr('b');
$handle->exec();
复制代码
为了查看这期间具体的连接过程,用wireshark监听回环地址端口6379,抓包请求如下图所示:
redis客户端与服务端建立连接后,multi标记事务开始,之后每次执行,服务端返回queued队列标志。查看redis源码src/multi.c文件:
void queueMultiCommand(client *c) {
multiCmd *mc;
int j;
c->mstate.commands = zrealloc(c->mstate.commands,
sizeof(multiCmd)*(c->mstate.count+1));
mc = c->mstate.commands+c->mstate.count;
mc->cmd = c->cmd;
mc->argc = c->argc;
mc->argv = zmalloc(sizeof(robj*)*c->argc);
memcpy(mc->argv,c->argv,sizeof(robj*)*c->argc);
for (j = 0; j < c->argc; j++)
incrRefCount(mc->argv[j]);
c->mstate.count++;
}
复制代码
在上述源码中可以看到redis服务端每次会把事务块中的命令保存到内存中,上述简介已经解释过最后通过exec命令执行,再看下面示例图的返回结果可以了解到redis服务端一次性返回所有命令执行返回结果。
pipeline
简介
客户端将执行的命令写入到缓冲中,最后由exec命令一次性发送给redis执行返回。
实现原理
同样,用相关代码调用redis抓包;
$redis = new redis();
$redis->connect('127.0.0.1',6379);
$handle = $redis->pipeline();
$handle->incr('a');
$handle->incr('b');
$handle->exec();
复制代码
继续用wireshark抓包,如下图所示
- pipeline 客户端请求包示例图
- pipeline 服务端返回包示例图
这上面的图片简要分析一下,pipeline管道操作是需要客户端与服务端的支持,客户端将命令写入缓冲,最后再通过exec命令发送给服务端,服务端通过命令拆分,逐个执行返回结果。
两者的区别
由上面的请求也可以看出了两者最明显的区别是客户端发送请求的方式不一样,具体相关区别如下:
- pipeline选择客户端缓冲,multi选择服务端缓冲;
- 请求次数的不一致,multi需要每个命令都发送一次给服务端,pipeline最后一次性发送给服务端,请求次数相对于multi减少
- multi/exec可以保证原子性,而pipeline不保证原子性
作者:Walker
链接:https://juejin.im/post/5b42e025f265da0fa332d4dc
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。