字符串的公共前缀对Mysql B+树查询影响回溯分析
上线之后,跟微信相关的用cid列的查询会话的SQL变慢了几十倍!思考这个问题思考了非常久。从出现以来一直是我心头的一个结。cid这一列是建了索引的,普通的cid列更新都没问题,为何仅仅有微信的有问题?同样的前缀又是怎样影响索引的?
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+树查询影响回溯分析的更多相关文章
- MySQL知识树-查询语句
在日常的web应用开发过程中,一般会涉及到数据库方面的操作,其中查询又是占绝大部分的.我们不仅要会写查询,最好能系统的学习下与查询相关的知识点,这篇随笔我们就来一起看看MySQL查询知识相关的树是什么 ...
- LeetCode 14. Longest Common Prefix字典树 trie树 学习之 公共前缀字符串
所有字符串的公共前缀最长字符串 特点:(1)公共所有字符串前缀 (好像跟没说一样...) (2)在字典树中特点:任意从根节点触发遇见第一个分支为止的字符集合即为目标串 参考问题:https://lee ...
- leetcode_最长公共前缀
题目:Write a function to find the longest common prefix string amongst an array of strings. 题解:给出的函数为: ...
- LeetCode 7最长公共前缀
编写一个函数来查找字符串数组中的最长公共前缀. 如果不存在公共前缀,返回空字符串 "". 示例 1: 输入: ["flower","flow" ...
- [LeetCode]14. Longest Common Prefix最长公共前缀
Write a function to find the longest common prefix string amongst an array of strings. If there is n ...
- LeetCode刷题-最长公共前缀(简单)
题目描述 编写一个函数来查找字符串数组中的最长公共前缀. 如果不存在公共前缀,返回空字符串 "". 示例 1: 输入: ["flower","flow ...
- 最长公共前缀 leetcode 14
方法一(纵向扫描) 解题思路 先计算出数组中最小的字符串长度,这样就避免了越界的情况,思路更加明确,但同时时间复杂度就相应的上升了. 先计算所有字符串在同一列上的字符是否相同,然后依次向后延伸. 代码 ...
- 每日一道 LeetCode (5):最长公共前缀
前文合集 每日一道 LeetCode 前文合集 代码仓库 GitHub: https://github.com/meteor1993/LeetCode Gitee: https://gitee.com ...
- Leetcode13. 罗马数字转整数Leetcode14. 最长公共前缀Leetcode15. 三数之和Leetcode16. 最接近的三数之和Leetcode17. 电话号码的字母组合
> 简洁易懂讲清原理,讲不清你来打我~ 输入字符串,输出对应整数  Memory Limit: 32768/32768 K (Java/Others)Total ...
- SGU 405 Totalizator
405. Totalizator Time limit per test: 0.25 second(s)Memory limit: 65536 kilobytes input: standardout ...
- Chrome在win8显示“没有注册类”的解决办法
问题1:从任务栏和桌面快捷方式无法打开Chrome,显示错误为没有注册类问题2:无法从word等中点击打开url,无法打开html,htm方式的文件,同样显示错误为没有注册类出了这两个错误后,用起来相 ...
- sql把一个表的某几列的数据存到另一个表里
一.如何用slq语句把一个表中的某几个字段的数据插入到另一个新表中,就要用下面这条slq语句: insert into 表名1(字段1,字段2) select 字段1,字段2 from 表名2 ...
- Java heap space 解决方法(转)
因为程序要从数据读取近10W行记录处理,当读到9W的时候就出现 java.lang.OutOfMemoryError: Java heap space 这样的错误. 在网上一查可能是JAVA的堆栈 ...
- 在JTextField中监听回车键,并执行相应按钮
在jbInit() 中加入jTextField1的按键监听,代码如下: jTextField1.addKeyListener(new KeyAdapter(){ public voi ...
- 不明白的sizeof(enum)数据结构存储问题
不明白的sizeof(enum)数据结构存储问题 typedef struct weekday_st { enum week {sun=123456789,mon,tue,wed,thu,fri,sa ...