phpredis、predis切库导致数据串库错乱

背景

新同事的加入,有新需求所以自己在 redis 中使用了不同的 db。是的,使用了 select 方法,然后坑就来了。

问题

在预发验证的时候发现了数据串库的问题,redis 默认的 db0 中出现了本应该在指定库出现的数据,导致程序一会可以读取,一会读取不到。而且本应该在默认库出现的数据也跑到了指定库中。

解决步骤

其实刚看到问题时,就能猜出一大半了。

① 首先估摸着是使用完 redis 连接后没有关闭导致,于是增加 close,再次验证。结果竟然不起作用!!🤔

② 之后想到 redis 的配置问题,redis 在连接时会有参数设置:pconnect(持久连接persistent),将之设置为 false,重启应用程序再次验证,问题解决。

原因分析

为什么会出现这个情况?在解决过程中 ① 的操作为啥没生效?

正常来说在php脚本执行时都是执行完后,连接和资源将释放(单一进程模式),但是当 redis 设置持久连接后(pconnect),该连接并不随脚本执行结束释放。

那该连接什么时候释放呢?答案是随着 php-fpm 的生命周期释放。参见:phpredis/phpredis: A PHP extension for Redis (github.com)

那我们的 close 是关闭了个寂寞么?不是的,关闭是让连接不在本进程内占用,归还到了连接池可以让其他进程复用。比如你的程序执行时间很长(需要一天),那么你使用完连接后立马关闭,可以防止这个连接被占用一天。

总结

  • pconnect 会在连接池里建立连接,不受 close 的影响。close 只影响当前实列是否继续使用该连接 当一次 php 脚本结束后,变量销毁时该连接会归还到连接池
  • pconnect连接的生命周期是 fpm 进程的生命周期,而非一次php的执行
  • connect 在使用的时候会建立一个连接,脚本结束后或者使用 close 会销毁连接
  • 当设置持久连接时close 并不会销毁连接 ,只是断开连接使当前 php 不能再进行 redis 请求,该连接在后续请求中仍然会被重用,直至 fpm 进程生命周期结束
  • 并不是使用了 pconnect 就不要 close 了,如果当前脚本执行时间很长会一直占用一个连接。养成及时释放资源的好习惯:请看鸟哥blog

相关文章:
redis的连接释放问题 – Starsea’s Note
深入php redis pconnect_一路向前ylc的博客-CSDN博客_redis pconnect

后续

当我们把 pconnect 改为 connect 后(长连接变为短连接),在压测时会出现新的问题。

“Cannot assign requested address”,其实原因也很简单,短连接在并发量较大时不能复用导致,tcp 连接频繁建立,客户端无法分配出新的端口。参见:使用短连接访问Redis出现“Cannot assign requested address”错误_分布式缓存服务 DCS_常见问题_客户端和网络连接_华为云 (huaweicloud.com)

所以生产环境务必改为长连接模式

最终方案是:
1、如果使用的 key 值不多就不要进行切库操作,添加相应的前缀即可
2、改用 CLI 命令行的执行方式,与 fpm 模式隔离

Author: thinkwei

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注