问题现象
最近调查一个ceph集群pg blocked IO/ slow request问题,发现集群出现短暂的blocked IO/ slow request,定位到日志发现在等待读写锁导致。查看当前集群负载,及前后日志均未发现异常,也没有慢盘情况。但有一个现象是操作前有deep-scrub 操作。猜测是PG deep 导致block IO。
问题原因
结合代码查看PG在做deep scrub时候确实会对chunk 加锁,导致IO下发到该PG内部的该chunk相关的对象读写阻塞,直到操作完毕,恢复读写;
pg id计算
以下是pg关键日志:
我们可以很清楚的看到pg: 3.ad8 在做deep-scub操作,但是在出现slow request日志中,只能找到异常的对象rbd_data.xxx,甚至更详细的信息off-len,无法看到pg 3.ad8,这样就无法确认该对象发生在pg 3.ad8上。
这里看到一个3.cc2fcad8 的字段,这个是本章介绍的重点。猜测这个 3.cc2fcad8 --- 3.ad8
存在映射关系。
其实ceph是通过C/S模式实现外部应用和内部集群之间的数据交互,这里对象是集群的基本单位,对象名字是rbd client负责生成,类似“rbd_data.xxx.xxxx”,计算出一个32位的哈希值(eg:cc2fcad8),然后根据对象的归属pool以及此哈希值,通过简单的取模计算,即可找到对应的PG。
pool id为3,查看该pool上有6030个pg,由于pg不是2的整数次方幂,向下取 2^(n-1)=4096
>python
>>> hex(0xcc2fcad8%4096)
'0xad8'
# hex 和 int函数是一个互逆函数;
>>> hex(255)
'0xff'
>>> int(0xff)
255
>>>
这里一般集群存储池pool的pg 是2的整数次方幂,此时计算直接 hex(0xcc2fcad8%pg_num) 即可,对于不是整数次方幂的情况,需要先向下取 2^(n-1),然后去计算。确保对象可以落在所有的pgid上。(其实对于非对齐方式,如果直接按照 2^n取模,会出现对象落在不存在pgid上的现象)
具体的计算方法:
if ((hash & (2^n -1)) < pg_num)
return (hash & (2^n -1));
else
return (hash & (2^n-1 -1))