年前项目组接微信公众号。

上线之后,跟微信相关的用cid列的查询会话的SQL变慢了几十倍!思考这个问题思考了非常久。从出现以来一直是我心头的一个结。cid这一列是建了索引的,普通的cid列更新都没问题,为何仅仅有微信的有问题?同样的前缀又是怎样影响索引的?

   分析过程
    1.explain下微信cid的查询。微信的cid会以mid-qqwanggou001为前缀插入数据
explain
select *
from analysis_sessions
where cid = "mid-qqwanggou001-b99359d9054171901c0"

分析结果例如以下:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" width="867" height="100" />

从explain分析能够看出。这个查询使用了索引,可是innodb觉得有165万行数据须要给mysqlserver筛选(也就是用where条件过滤)。

假设这些庞大的数据在内存,遍历一遍花不了多少时间。可是极有可能,这些数据是在磁盘上的。这么多的数据从磁盘读取然后加载内存。大量磁盘IO必定是十分的耗时的。

相比内存的电子运动。磁盘机械臂的物理运动要慢好几个数量级。

2.分析普通cid的查询

取数据进行explain。cid = "sid-a2f9047ddf528d837e5f60843c83aae9"。这个数据是不带公共前缀的。

   
explain
select *
from analysis_sessions
where cid = "sid-a2f9047ddf528d837e5f60843c83aae9"

分析结果例如以下:

同样的列,同样的索引。这次存储引擎向mysqlserver仅仅返回了一行数据。也就是说innodb仅仅须要读取一个二级索引的叶子节点。

相对于上面那个sql的IO,压力显然小非常多。

初步分析结论:带有长前缀的cid查询。innodb存储引擎会向mysql上端server返回百万级别的数据。

这仅仅是现象,我还是想问,同样的表,同样的列,同样的索引结构(B+树索引)。同样的查询,仅仅不同的数据。结果为何有差么大的区别?

近一步分析

纠结这个问题非常久了,直到前天晚上散步时候。无意的会想到了 explain结果的key_len这一列。这一列我从来不看,觉得没用。可是27与cid这一列50个varchar的定义格格不入。27明显小于50,首先能够肯定,这个索引用的是前缀索引,说白了,截取了字符串的前面一部分作为索引数据。analysis_session表用的gbk编码。也就是说,索引须要2个字节表示一个varchar。解释一下key_len

27 = 2 * 12 + 2 + 1

27位的索引,仅仅索引了前面12个字符。中间的2存储长度。后面的一个字节存储Null信息,由于这一列是同意Null的。

终于结论:问题到这已经非常明了了,微信cid的前缀是17个字符的,大于前缀索引的12个字符,也就是说。全部存储微信cid数据(百万级别)B+树叶子节点将仅仅有一个B+树非叶节点的指针指向这里。于是。当你查微信cid相关的数据时,全部微信cid将被返回给mysqlserver进行where过滤了,效率上讲,这是非常恐怖的。

索引确实还是被用上了。不然会造成全表扫描。可是这个数据设计的有问题。B+树的查找效率是O(LogN)的,可是遇上这个数据,立马变成O(N),相当于一个局部全表扫描。

那么合理的猜測。仅仅要有新增的微信cid,微信cid的查询仅仅会变的更慢。

引申,更佳的代码 practice:

varchar,blob, text等边长数据建索引的时候。数据库会自己主动建前缀索引,于是B+树不会索引整个字段的部分。非常多同学喜欢用前缀作为字符串的标志,这次要注意了,有前车之鉴了。前缀存入mysql之后会减少检索效率,前缀越长。B+树查询的效率越低。

这里给出代码的建议:

1.将前缀作为后缀,startWith改为endWith

2.不要尝试后缀模糊搜索,like "%.com",这样的做法更糟糕,全然用不了索引,于是全表扫描。

字符串的公共前缀对Mysql B+树查询影响回溯分析的更多相关文章

  1. MySQL知识树-查询语句

    在日常的web应用开发过程中,一般会涉及到数据库方面的操作,其中查询又是占绝大部分的.我们不仅要会写查询,最好能系统的学习下与查询相关的知识点,这篇随笔我们就来一起看看MySQL查询知识相关的树是什么 ...

  2. LeetCode 14. Longest Common Prefix字典树 trie树 学习之 公共前缀字符串

    所有字符串的公共前缀最长字符串 特点:(1)公共所有字符串前缀 (好像跟没说一样...) (2)在字典树中特点:任意从根节点触发遇见第一个分支为止的字符集合即为目标串 参考问题:https://lee ...

  3. leetcode_最长公共前缀

    题目:Write a function to find the longest common prefix string amongst an array of strings. 题解:给出的函数为: ...

  4. LeetCode 7最长公共前缀

    编写一个函数来查找字符串数组中的最长公共前缀. 如果不存在公共前缀,返回空字符串 "". 示例 1: 输入: ["flower","flow" ...

  5. [LeetCode]14. Longest Common Prefix最长公共前缀

    Write a function to find the longest common prefix string amongst an array of strings. If there is n ...

  6. LeetCode刷题-最长公共前缀(简单)

    题目描述 编写一个函数来查找字符串数组中的最长公共前缀. 如果不存在公共前缀,返回空字符串 "". 示例 1: 输入: ["flower","flow ...

  7. 最长公共前缀 leetcode 14

    方法一(纵向扫描) 解题思路 先计算出数组中最小的字符串长度,这样就避免了越界的情况,思路更加明确,但同时时间复杂度就相应的上升了. 先计算所有字符串在同一列上的字符是否相同,然后依次向后延伸. 代码 ...

  8. 每日一道 LeetCode (5):最长公共前缀

    前文合集 每日一道 LeetCode 前文合集 代码仓库 GitHub: https://github.com/meteor1993/LeetCode Gitee: https://gitee.com ...

  9. Leetcode13. 罗马数字转整数Leetcode14. 最长公共前缀Leetcode15. 三数之和Leetcode16. 最接近的三数之和Leetcode17. 电话号码的字母组合

    > 简洁易懂讲清原理,讲不清你来打我~ 输入字符串,输出对应整数 ![在这里插入图片描述](https://img-blog.csdnimg.cn/63802fda72be45eba98d9e4 ...

随机推荐

  1. Codeforces Round #280 (Div. 2) E. Vanya and Field 思维题

    E. Vanya and Field time limit per test 2 seconds memory limit per test 256 megabytes input standard ...

  2. android 从零单排 第一期 按键显示helloworld

    啦啦啦- 我是qscqesze 今天开始android的从零单排啦啦啦- 首先从最简单的开始 要求: 程序运行后,单击屏幕上的按键后可以显示一句话,如“Hello World!” 这是一个最基础最基础 ...

  3. Asky极简教程:零基础1小时学编程,已更新前8节

    Asky极简架构 开源Asky极简架构.超轻量级.高并发.水平扩展.微服务架构 <Asky极简教程:零基础1小时学编程>开源教程 零基础入门,从零开始全程演示,如何开发一个大型互联网系统, ...

  4. Oracle | 给表和字段添加注释

    comment  on  column  表名.字段名   is  '注释内容'; comment on column OPERATOR_INFO.MAIN_OPER_ID is '归属操作员'; c ...

  5. 3.3V与5V电平双向转换

    http://www.amobbs.com/thread-5541344-1-2.html 3.3V转5V:S3输入为0V时,NMOS管导通,S5=S3=0V:S3输入为3.3V时,NMOS管截止,S ...

  6. XStream转换Java对象与XML

    1.引入需要的jar包,在pom.xml中配置依赖 <dependency> <groupId>com.thoughtworks.xstream</groupId> ...

  7. [c#]WebClient异步下载文件并显示进度

    摘要 在项目开发中经常会用到下载文件,这里使用winform实现了一个带进度条的例子. 一个例子 using System; using System.Collections.Generic; usi ...

  8. js判断手机端和pc端

    var browser = { versions: function() { var u = navigator.userAgent, app = navigator.appVersion; retu ...

  9. 【Nginx】ngx_event_core_module模块

    ngx_event_core_module模块属于事件模块,它是其他事件类模块的基础.它主要完毕下面任务: 创建连接池 决定使用哪些事件驱动机制 初始化将要使用的事件模块 以下分析该模块的代码. ng ...

  10. 使用Bootstrap 3开发响应式网站实践07,页脚

    页脚部分比较简单,把一个12列的Grid切分. <footer> <div class="container"> <div class="r ...