博客密码找回来了,心血来潮,又准备写写了。。
前几日在使用我们自己修改过的多线程redis的过程中, 出现几千qps就把redis压死了的情况。。与预期严重不符。
我们使用的时候每个key,value都会设置一个ttl,而这个ttl很段,一般几十秒到几分钟
现象:
1. 登陆上去瞅了下mpstat 命令看到各cpu %50左右都处于%sys,
2. sar 命令看到每秒收包1万个,但是出包只有几百个。
3. 通过perf top看到锁占用非常高
进入看发现
可以看到这个是个系统调用产生的,
可以看到这个锁在内核态,
问题在于我们修改之后的redis中也没有什么地方调用这种锁,关键是我们调用锁的地方,perf里面也没有显示。
那么说明我们使用的redis程序中,
某个函数可能通过glibc发起了锁操作的系统调用
也就是说我们的程序不一定使用了锁,但是通过glibc提供的某个函数,导致了锁的产生
进到uRedis程序中,看到
瓶颈在于dictGetRandomKey这个函数,
查看代码发现activeExpireCycle会调用
dictGetRandomKey,而dictGetRandomKey又会调用glibc的random()函数,
难道是glibc的这个random()函数有问题?
网上随便搜了下,有讲到glibc里面random() 函数使用了锁,good,接下来需要验证一下准确性
rpm -qa|grep glibc 查看到我们当前使用的是glibc-2.12,下载glibc-2.12的tar包下来,查找random()函数到达咋写的
good,glibc的random()函数是__random()的别名,最终调用上面这段代码
基本可以确定就是这个问题了。
问题归结为:
我们使用的多线程redis,每个线程在处理过期key的时候,每个线程通过redis的函数
dictGetRandomKey调用了glibc的random()函数,而random()函数里面有锁导致各线程阻塞,内核态自旋锁飙高,%sys就高了。
问题明确了,那么我们可以修改
dictGetRandomKey()函数去掉对random()的调用,改为顺序从头到尾调用(每次记住上次更新的位置),或者每个线程搞一个random变量,再启动一个线程,专门调用glibc()的random(),把结果不停的写到各线程的random变量里边去就行了。
修改了下,问题解决,服务恢复正常,进出包基本持平,整体cpu利用率%1.5左右。
问题:我们在压力测试的时候关注set key对应的ttl 设置和实际运用不太一样,测试的时候只set/get key,value,结果流量一上来就出问题了。。前期工作没做好。。
阅读(4397) | 评论(0) | 转发(0) |