redis报protocol error的真正原凶

本文涉及的产品
云原生内存数据库 Tair,内存型 2GB
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Redis 版,经济版 1GB 1个月
简介:

前段时间写了个文章详细描述了在什么场景下会出现redis的protocol error错误,但是手抽筋, 不小心点错给删了,而且还原不了,没办法了,只能重写一下,但是没上次那么详细了,如果不太明白就看源代码吧!首先呢,这种错误是基于使用了phpredis的的长连接和multi功能才会出现!这里有两个问题

1、当你开了事务,做了N次写操作,然后又discard之后又做了M次操作(M小于N),这样请求就会被阻塞住(这个操作无论使用短连接还是长连接,都能复现),具体看代码:

$redis = new Redis();
$redis->connect('localhost', 6379);
$redis->multi();
$redis->set('test', 10);
$redis->zIncrBy('test2', 1, 'bbb');
$redis->discard();
$redis->multi();
$redis->zIncrBy('test2', 2, 'bbb');
$redis->exec();//操作会阻塞在这里

因为phpredis在discard成功后,没有清理callback list,所以卡住了。

2、开事务,做N次操作,discard之后再做M次操作(但这里M大于N),多刷几次就会出现protocol error(这个必须使用长连接才会复现)!

$redis = new Redis();
$redis->pconnect('localhost', 6379);
$redis->multi();
$redis->set('test', 10);
$redis->discard();
$redis->multi();
$redis->zIncrBy('test2', 2, 'bbb');
$redis->zIncrBy('test2', 1, 'bbb');
$redis->exec();

跟上面原因一样,discard没的清理callback list, 就会出现stream里面的数据没读完!协议就完全乱掉了,

那么上面说的callback list又是什么东西呢?在redis里面,当你使用了multi,在执行exec之前的请求基本都是返回+QUEUED(如果需要了解更详细redis协议,请见redis.io),而真正返回数据是等exec执行之后,才去解析返回数据。所以phpredis针对不同的请求处理方式是不一样的,所以在开启了multi之后,phpredis会维护一个处理函数列表,比如set(k,v)这需要绑定一个bool值处理函数,而zincrBy需要绑定一个double值处理函数,执行exec之后,去遍历这个列表处理返回数据就即可。

由于redis针对multi之后的请求都是队列并没有执行,所以客户端可以使用discard命令来清空这个队列,同时客户端也应该将之前绑定的函数列表一并清除,可是phpredis对于discard的处理仅仅是发送了discard命令到redis服务端,却没有清空处理函数列表。只是在下一次执行multi的时候,他仅仅是将这个处理函数列表中一个叫current的指针值为NULL(这个列表是一个单向连表,head表示头元素,current表示尾元素),可是他忽略了head,因为函数列表一旦进入处理是从head开始,只有需要新加函数到列表的时候才会用到current。所以在phpredis里面,执行一条命令,再discard,再执行两条命令之后,这个处理函数列表里只有一个函数(正确应该是两个,而且还是最开始加的那一个,后面加的两个,不翼而飞了。。),处理函数与返回数据不配对,协议自然也就乱了,这就是protocol error报错的由来....

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
2月前
|
NoSQL 网络安全 Redis
redis.exceptions.ConnectionError: Error 111 connecting to 127.0.0.1:6379. Connection refused.
当使用Python连接Redis遇到"ConnectionError: Error 111"时,可能的原因包括Redis未启动、非默认端口监听、防火墙阻拦、配置错误或Redis模块安装不正确。解决方法包括启动Redis、检查端口与防火墙设置、修正配置文件、确保模块正确安装及测试服务器功能。提供了一个Python连接Redis的示例代码,根据实际情况调整IP和端口,以诊断连接问题。
134 0
|
2月前
|
存储 NoSQL Redis
redis-(error)-MISCONF。Redis。is。configuredto。save。RDBsnapshots
redis-(error)-MISCONF。Redis。is。configuredto。save。RDBsnapshots
227 0
|
2月前
|
NoSQL 安全 Linux
Another Redis Desktop Manager远程连接Redis报错:Client On Error: Error: connect ETIMEDOUT
在尝试使用Another Redis Desktop Manager连接远程Redis时遇到持续Timeout的问题,检查并执行了常规教程中的所有步骤,包括修改Redis配置文件以允许远程访问,开放本地防火墙的6379端口,以及确保网络连通性。
64 0
|
9月前
|
NoSQL Java Redis
redis.clients.jedis.exceptions.JedisDataException: ERR Syntax error, try CLIENT (LIST | KILL ip:port
redis.clients.jedis.exceptions.JedisDataException: ERR Syntax error, try CLIENT (LIST | KILL ip:port
|
2月前
|
NoSQL Redis
进行主从复制时出现的异常FATAL CONFIG FILE ERROR (Redis 6.2.6)Reading the configuration file
进行主从复制时出现的异常FATAL CONFIG FILE ERROR (Redis 6.2.6)Reading the configuration file
149 0
|
8月前
|
前端开发 Java 数据库连接
ERROR o.s.d.redis.listener.RedisMessageListenerContainer
ERROR o.s.d.redis.listener.RedisMessageListenerContainer
155 0
|
NoSQL Redis 数据安全/隐私保护
Docker进入redis容器连接redis-cli 报错:(error) NOAUTH Authentication required.
Docker进入redis容器连接redis-cli 报错:(error) NOAUTH Authentication required.
|
NoSQL Linux 网络安全
关于Redis的远程连接 Connection: Disconnect on error 问题
关于Redis的远程连接 Connection: Disconnect on error 问题
|
Linux C语言
Centos7安装redis6.0.7出错:make[1]: *** [server.o] Error 1 make[1]: Leaving directory `/opt/redis-6.0.7/s
Centos7安装redis6.0.7出错:make[1]: *** [server.o] Error 1 make[1]: Leaving directory `/opt/redis-6.0.7/s
Centos7安装redis6.0.7出错:make[1]: *** [server.o] Error 1 make[1]: Leaving directory `/opt/redis-6.0.7/s