上一篇,我们总结了QC的主要瓶颈在:QC锁竞争;表的permission check锁竞争;查询时Hash碰撞。
因此,就可以初步决定了我们的优化方向:QC锁优化,表permission check的锁优化(不能影响Buffer pool),Hash碰撞的优化。
本篇我们尝试在Hash碰撞上面进行优化。
MySQL原生Hash算法行
MySQL的的原生Hash算法,是在http://Sql_cache.cc中的init_cache方法中进初始化的:
图1 hash算法初始化的地方
继续跟踪查找后,看到实际上调用了_my_hash_init方法,其中hash->hash_function就是初始化算法的地方,如果没有自定义,则会使用cset_hash_sort_adapter这个方法。在向其中查找,最终可以找到默认的hash方法是ctype-bin.c文件中的my_hash_sort_bin方法
图2 原始的hash算法
这是普通的字符串Hash算法,这种算法过于简单,重复率非常高;比如使用Aurora的测试模型时,碰撞率非常高,一次my_hash_search需要查找40+次,因此才会出现上期所说hash碰撞问题。那么有没有好的Hash算法,可以降低碰撞率呢?这里就要介绍一下MurmurHash算法
MurmurHash是高运算性能,低碰撞率,由Austin Appleby创建于2008年,现已应用到Hadoop、libstdc++、nginx、libmemcached, redis等开源系统。2011年Appleby被Google雇佣,随后Google推出其变种的CityHash算法
MurmurHash 是一种非加密型哈希函数,适用于一般的哈希检索操作。 由Austin Appleby在2008年发明, 并出现了多个变种,都已经发布到了公有领域(public domain)。与其它流行的哈希函数相比,对于规律性较强的key,MurmurHash的随机分布特征表现更良好。---摘自wiki
具体的算法各位可以在网上查到,说明很多,本文具体说明如何修改源码。
添加新的Hash算法
在sql_cache.h文件中新增hash算法:
图3 算法
图4 另外加上入口
修改注册算法
在http://sql_cache.cc文件中,添加注册hash的handler
图5 需要注册的hash handler
然后修改文章开头提到的my_hash_init方法所在的地方:
图6 修改注册的地方
注意这里使用了my_hash_init3这个宏,大家可以看到,实际上都是调用了_my_hash_init方法,只是入参不同。这里使用第三个宏,才可以吧自己的方法注册进去。
另外这里还要非常住一个值的修改:图5中QUERY_CACHE_DEF_QUERY_HASH_SIZE这个参数,默认是1024,这里改为200000。通过算法可以看到最终的hash值如果跟1024取余的话,那相当于只有1024种取值,那么碰撞会非常大。笔者在这里踩了1个多小时的坑,刚修改完hash算法后,bench mark越跑越低。
以上代码修改完成后,编译运行,来对比下优化前和优化后的数据:
图7 优化前
图8 优化后
从以上数据对比可以看到:优化前只有6500~6600左右的纯读QPS,跑久了也就7000左右;而优化后,已经到1W左右的QPS,一个小小的改动,提升了将近50%的性能!
再来看下是否真的是hash算法消耗的时间降低了呢?
图9 优化前的my_hash_search消耗时间占比
图10 优化后的my_hash_search消耗占比
从图9和图10的对比中我们可以看到,my_hash_search方法消耗的时间占比大大降低,同时读时延也大大减少。新的hash算法带了较低的hash