5-04 MySQL索引优化实战一
2022-12-13 00:28:02 0 举报
04 Mysql索引优化实战一
作者其他创作
大纲/内容
<b>实战应用示例表</b><font color="#9e9e9e">(以下所有示例使用此表)</font>
1. 创建示例表
2. 手动插入一些数据
3. 通过存储过程插入海量数据
<b>难以理解的综合示例</b>
<b><font color="#2196f3">1、联合索引第一个字段用范围不会走索引</font></b>
如下所示:
<b><font color="#4caf50">结论:</font></b>联合索引第一个字段就用范围查找不会走索引,MySQL内部可能觉得<b>第一个字段就用范围</b>,<font color="#e57373"><b>结果集应该很大</b>,<b>回表效率不高</b></font>,还<b><font color="#4caf50">不如就全表扫描</font></b>
<b><font color="#2196f3">2、强制走索引</font></b>
如下所示:
与默认的<b style="color: rgb(76, 175, 80);">联合索引第一个字段用范围不会走索引</b><font color="#000000"><b>对比</b>:</font>
如下所示:
由上可看出,<b>强制走索引</b>后预估的<b>扫描行数相对未走索引的少</b>了点儿
但根据实际的执行时间比较,<b><font color="#000000">强制走索引查询</font></b>耗时比较多,因为走索引需要一个回表操作,<b><font color="#e57373">回表效率不高</font></b>,<b><font color="#e57373">影响了整体查询效率</font></b>
<b><font color="#2196f3">3、覆盖索引优化</font></b>
如下所示:
<b><font color="#4caf50">结论:</font>针对</b>以上<b><font color="#e57373">范围查询不走索引</font></b>情况,可以<b><font color="#4caf50">采用覆盖索引</font></b>进行优化
<b><font color="#2196f3">4、in和or在表数据量比较大的情况会走索引,在表记录不多的情况下会选择全表扫描</font></b><br>
如下所示1:<font color="#9e9e9e">(数据量比较大时,in查询走索引)</font>
如下所示2:<font color="#9e9e9e">(数据量比较小时,in查询全表扫描)</font>
如下所示3:<font color="#9e9e9e">(数据量比较大时,or查询走索引)</font>
如下所示4:<font color="#9e9e9e">(数据量比较小时,or查询全表扫描)</font>
<b><font color="#4caf50">结论:</font></b>在<b><font color="#4caf50">数据量比较大时</font></b>,<b><font color="#2196f3">in或or查询</font></b>,<b><font color="#4caf50">全表扫描元素量较大</font></b>,<b><font color="#e57373">效率不如走索引</font></b>;<b>反之</b>,<b><font color="#4caf50">数据量小时</font></b>,<b><font color="#4caf50">全表扫描少了一步回表</font></b>操作,相对<b><font color="#4caf50">比走索引效率高</font></b>
<b><font color="#2196f3">5、like KK% 一般情况都会走索引</font></b>
如下所示1:<font color="#9e9e9e">(大数据量时,走索引)</font>
如下所示2:<font color="#9e9e9e">(小数据量时,走索引)</font>
这里需要补充一个概念,<b><font color="#00bcd4">索引下推(Index Condition Pushdown,ICP)</font></b>, <b><font color="#4caf50">like KK%其实就是用到了索引下推优化</font></b><br>
<b><font color="#2196f3">索引下推(Index Condition Pushdown,ICP)</font></b>
<b><font color="#f57c00">什么是索引下推了?</font></b>
<b>对于辅助的联合索引</b>(name,age,position),正常情况<b>按照最左前缀原则</b>
如上述 <b>like KK% </b>语句为例,这种情况<b><font color="#e57373">只会走name字段索引</font></b>
因为<b>根据name字段过滤完</b>,<b><font color="#e57373">得到的索引行里的 age 和 position 是无序的</font></b>,无法很好的利用索引
在<b>MySQL5.6之前</b>的版本,这个查询:
1)只能在联合索引里匹配到名字是 'LiLei' 开头的索引
2)然后拿这些索引对应的主键逐个回表
3)到主键索引上找出相应的记录
4)再比对age和position这两个字段的值是否符合
<b>MySQL 5.6引入了索引下推优化</b>
<b><font color="#2196f3">索引下推</font></b>:可以<b>在索引遍历过程中</b>,<b>对索引中包含的<font color="#2196f3">所有字段</font>先做判断</b>,<b>过滤掉不符合条件的记录之后再回表</b>,可以<b><font color="#2196f3">有效的减少回表次数</font></b>。
使用了索引下推优化后,上面那个查询:
1)在联合索引里匹配到名字是 'LiLei' 开头的索引之后
2)同时还会在索引里过滤age和position这两个字段
3)拿着过滤完剩下的索引对应的主键id再回表查整行数据
<b><font color="#2196f3">索引下推</font><font color="#4caf50">会减少回表次数</font></b>
<b>对于innodb引擎</b>的表<b><font color="#2196f3">索引下推</font><font color="#9c27b0">只能用于二级索引</font></b>
<b>innodb的主键索引(聚簇索引)</b>树叶子节点上保存的是全行数据,所以这个时候<b><font color="#2196f3">索引下推</font><font color="#e57373">并不会起到减少查询全行数据</font></b>的效果
<b><font color="#f57c00">为什么</font><font color="#000000">范围查找</font><font color="#f57c00">MySQL没有用索引下推优化?</font></b><br>
估计应该是MySQL认为<b>范围查找<font color="#e57373">过滤的结果集过大</font></b>
<b>like KK%</b> 在<font color="#f57c00">绝大多数情况</font>来看,<b style="color: rgb(76, 175, 80);">过滤后的结果集比较小</b><font color="#9e9e9e">(当然这也不是绝对的,有时like KK% 也不一定就会走索引下推)</font>
<b>MySQL如何选择合适的索引</b>
如下所示1:<font color="#9e9e9e">(范围查询,未走索引)</font>
<b>如果用name索引<font color="#e57373">需要遍历name字段联合索引树</font></b>,然后<b><font color="#e57373">还需要根据遍历出来的主键值去主键索引树里再去查出最终数据</font></b>,<b>成本比全表扫描还高</b>
可以<b>用<font color="#2196f3">覆盖索引</font>优化</b>,这样<b>只需要遍历name字段的联合索引树就能拿到所有结果</b>
如下所示2:<font color="#9e9e9e">(范围查询,使用覆盖索引优化)</font>
如下所示3:<font color="#9e9e9e">(范围查询,修改查询条件,走了索引)</font>
对于上面这两种<b> name>'a'</b> 和<b> name>'zzz' </b>的执行结果:
<b>MySQL</b>最终<b>是否选择走索引</b>或者<b>一张表涉及多个索引</b>,<b><font color="#f57c00">MySQL最终如何选择索引?</font></b>
可以用<b>trace工具</b>来一查究竟,<b><font color="#e57373">开启trace工具会影响mysql性能</font></b>,所以<font color="#2196f3"><b style="">只能临时分析sql使用</b>,<b style="">用完之后立即关闭</b></font>
<b>trace工具用法</b>
<b>1. 开启trace:</b><br>
如下所示:<font color="#9e9e9e">(此工具会影响mysql性能,用完记得关哦)</font>
<b>2-1. 执行查询语句并查看trace信息1:</b><font color="#9e9e9e" style=""></font><font color="#9e9e9e" style=""></font>
如下所示:<font color="#9e9e9e">(查询name > 'a',</font><font color="#e57373">注意查看trace需要与查询语句一起执行才会生成trace内容</font><font color="#9e9e9e">)</font>
trace信息分析:<font color="#9e9e9e">(使用Json工具查看)</font>
<b><font color="#4caf50">结论:</font>全表扫描的成本</b><font color="#2196f3"><b>低于</b></font><b>索引扫描</b>,所以MySQL<b style=""><font color="#000000">最终选择</font><font color="#2196f3">“全表扫描”</font></b><br>
<b>2-2. 执行查询语句并查看trace信息2:</b>
<b><font color="#9c27b0">mysql> </font><font color="#0000ff">SELECT * FROM information_schema.OPTIMIZER_TRACE;</font></b>
如下所示:<font color="#9e9e9e">(查询name > 'zzz',</font><font color="#e57373">注意查看trace需要与查询语句一起执行才会生成trace内容</font><font color="#9e9e9e">)</font>
trace信息分析:<font color="#9e9e9e">(同上2-1内容结构,此处略。。)</font>
<b>结论:</b>可知<b>索引扫描的成本<font color="#2196f3">低于</font>全表扫描</b>,所以mysql<b><font color="#000000">最终选择</font><font color="#2196f3">“索引扫描”</font></b>
<b>3. 关闭trace:</b>
如下所示:
<b>常见SQL深入优化</b>
<b><font color="#f57c00">Order by 与 Group by优化</font></b>
<b><font color="#f57c00">示例1:</font></b>
如下所示:<font color="#9e9e9e">(查找用到了name索引和position索引,order by 排序使用了age索引)</font>
<b><font color="#4caf50">分析:</font></b>
<b>利用<font color="#2196f3">最左前缀法则</font></b>:中间字段不能断,因此<b>查询用到了name索引</b>
从<b><font color="#2196f3">key_len=74</font></b>也能看出,<b>age索引列用在排序过程中</b>,因为<b><font color="#2196f3">Extra</font></b>字段里<b><font color="#2196f3">没有using filesort</font></b><br>
<b><font color="#f57c00">示例2:</font></b>
如下所示:<font color="#9e9e9e">(查找使用了name索引,order by 排序未使用索引,因跳过了联合索引的age索引字段)</font>
<b><font color="#4caf50">分析:</font></b>
从 <b>key_len=74 </b>能看出,查询<b>使用了name索引</b>
由于<b>用了position进行排序</b>,<b><font color="#e57373">跳过了age</font></b>,出现了 <b><font color="#f57c00">Using filesort</font></b>
<b><font color="#f57c00">示例3:</font></b>
如下所示:<font color="#9e9e9e">(查找使用了name索引,order by 排序使用了 age 索引和 position 索引)</font>
<b><font color="#4caf50">分析:</font></b>
<b>查找只用到索引name</b>,<b>age和position用于排序</b>,<font color="#2196f3"><b>无Using filesort</b></font>
<b><font color="#f57c00">示例4:</font></b>
如下所示:<font color="#9e9e9e">(查找使用了name索引,order by 排序未使用索引,因排序字段顺序与索引字段顺序不一致)</font>
<b><font color="#4caf50">分析:</font></b>
和Case 3中explain的执行结果一样,但是<b><font color="#e57373">出现了Using filesort</font></b>
因为<b>索引的创建顺序为name、age、position</b>,但是<b><font color="#e57373">排序的age和position顺序颠倒了</font></b>
<b><font color="#f57c00">示例5:</font></b>
如下所示:<font color="#9e9e9e">(查询使用了name索引和age索引,在排序中被优化,排序使用了索引)</font>
<b><font color="#4caf50">分析:</font></b>
与Case 4对比,在Extra中并<b><font color="#4caf50">未出现 Using filesort</font></b>
因为<b>age为常量</b>,<b>在排序中被优化</b>,所以<b><font color="#2196f3">索引未颠倒</font></b>,<b><font color="#2196f3">不会出现 Using filesort</font></b>
<font color="#f57c00">示例6:</font>
如下所示:<font color="#9e9e9e">(查找使用了name索引,排序中position使用降序,导致与索引方式不一致,未使用到索引)</font>
<b><font color="#4caf50">分析:</font></b>
<b>虽然排序的字段列与索引顺序一样</b>,且 order by 默认升序
这里<b>position desc变成了降序</b>,<b><font color="#e57373">导致与索引的排序方式不同</font></b>,从而<b><font color="#e57373">产生Using filesort</font></b>
<b>MySQL 8.0 以上版本</b><font color="#2196f3"><b>有降序索引可以支持该种查询方式</b></font>
<font color="#f57c00">示例7:</font>
如下所示:<font color="#9e9e9e">(查找使用了name索引,但排序未使用索引)</font>
<b><font color="#4caf50">分析:</font></b>
<b>对于排序来说</b>,<b><font color="#e57373">多个相等条件也是范围查询</font></b>
<font color="#f57c00">示例8:</font>
如下所示1:<font color="#9e9e9e">(使用范围查询,查找和排序都未使用索引)</font>
如下所示2:<font color="#9e9e9e">(使用覆盖索引优化后,查找和排序都使用了索引)</font>
<b><font color="#4caf50">分析:</font></b><br>
<b>可以用<font color="#2196f3">覆盖索引</font><font color="#4caf50">优化</font></b>
<b><font color="#4caf50">优化总结:</font></b>
<b>1、MySQL支持两种方式</b>
<font color="#e57373" style="">Using filesort排序:</font>
1)先将要排序的数据分割,分割成每块数据都可以放到 sort_buffer 中
2)对每块数据在 sort_buffer 中进行排序,排序好后,写入某个临时文件中
3)当所有的数据都写入临时文件后,这时对于每个临时文件而言,内部都是有序的,但是它们并不是一个整体,整体还不是有序的,所以接下来就得合并数据
4)假设现在存在 tmpX 和 tmpY 两个临时文件,这时会从 tmpX 读取一部分数据进入内存,然后从 tmpY 中读取一部分数据进入内存。<font color="#f57c00">为什么是一部分而不是整个或者单个?</font>
因为首先磁盘是缓慢的,所以尽量每次多读点数据进入内存,但是不能读太多,因为还有 buffer 空间的限制
<font color="#4caf50">Using index排序</font>
是指MySQL扫描索引本身完成排序
<font color="#4caf50">Using index效率高</font>,<font color="#e57373">Using filesort 效率低</font>
<b>2、order by满足两种情况会使用Using index</b>
1) order by语句<font color="#2196f3">使用索引最左前列</font>
2) 使用where子句与order by子句条件列<font color="#2196f3">组合满足索引最左前列</font>
<b>3、尽量在索引列上完成排序,遵循索引建立(索引创建的顺序)时的最左前缀法则</b>
<b>4、<font color="#e57373">如果order by的条件不在索引列上,就会产生Using filesort</font></b>
<b>5、<font color="#2196f3">能用覆盖索引尽量用覆盖索引</font></b>
<b>6、group by与order by很类似</b>
<font color="#f57c00">其实质是先排序后分组,遵照索引创建顺序的最左前缀法则</font>
对于groupby的优化如果<font color="#2196f3">不需要排序的可以加上order by null禁止排序</font>
<font color="#f44336">注意</font>,<font color="#f57c00">where高于having</font>,<font color="#2196f3">能写在where中的限定条件</font>就<font color="#e57373">不要去having限定了</font>。
<b>Using filesort文件排序原理详解</b>
<b><font color="#2196f3">filesort文件排序方式</font></b>
<b>单路排序:</b>
是<b>一次性取出满足条件行的所有字段</b>,然后在<b>sort buffer中进行排序</b><br>
用<b>trace工具</b>可以看到<b>sort_mode</b>信息里显示<b>< sort_key, additional_fields ></b>或者<b>< sort_key, packed_additional_fields ></b>
<b>双路排序(又叫回表排序模式):</b>
首先根据相应的条件<b>取出相应的排序字段</b>和可以<b>直接定位行数据的行ID</b>
然后在 <b>sort buffer 中进行排序,排序完后</b>需要<b>再次取回其它需要的字段</b>
用<b>trace工具</b>可以看到<b>sort_mode</b>信息里显示<b>< sort_key, rowid ></b>
<b><font color="#f57c00">filesort如何判断使用哪种排序模式?</font></b>
MySQL 通过<b><font color="#ff9800">比较</font>系统变量</b> <b style="color: rgb(0, 0, 255);">max_length_for_sort_data</b><font color="#9e9e9e">(默认1024字节)</font><b><font color="#0000ff">的大小</font></b>和<b><font color="#00bcd4">需要查询的字段总大小</font></b>
如果 <font color="#00bcd4"><b>字段的总长度</b></font><b><font color="#f57c00">小于</font><font color="#0000ff">max_length_for_sort_data</font></b> ,那么使用 <b>单路排序 </b>模式
如果<font color="#00bcd4"> </font><b><font color="#00bcd4">字段的总长度</font><font color="#f57c00">大于</font><font color="#0000ff">max_length_for_sort_data </font></b>,那么使用 <b>双路排序 </b>模式
<b><font color="#2196f3">“单路排序”</font><font color="#000000">与</font><font color="#2196f3">“双路排序”</font>示例</b>:
<b>1)查询排序语句</b><b>:</b><font color="#9e9e9e">(排序未使用索引,使用了filesort)</font>
<b>2)开启trace工具</b>
如下所示:<font color="#9e9e9e">(用完记得及时关哦)</font>
<b>3-1)执行查询语句与查询trace信息1</b><font color="#9e9e9e" style="">(</font><font color="#9e9e9e">使用 单路排序 模式)</font>
如下所示:<font color="#9e9e9e">(默认max_length_for_sort_data=1024)</font>
查看trace内容:<font color="#9e9e9e">(只展示排序部分,trace信息使用Json工具查看)</font>
<font color="#4caf50"><b>结论:</b></font>字段的总长度<b>小于</b>max_length_for_sort_data,使用 <b>单路排序 </b>模式
<b>3-2)执行查询语句与查询trace信息2</b><font color="#9e9e9e">(使用 双路排序 模式)</font>
如下所示1:<font color="#9e9e9e">(设置set max_length_for_sort_data = 10,employees表所有字段长度总和肯定大于10字节)</font>
如下所示2:<font color="#9e9e9e">(查询trace结果)</font>
查看trace内容:<font color="#9e9e9e">(只展示排序部分,trace信息使用Json工具查看)</font>
<b><font color="#4caf50">结论:</font></b>字段的总长度<b>大于</b>max_length_for_sort_data,使用 <b>双路排序 </b>模式
<b>4)关闭trace工具</b>
如下所示:
<b><font color="#1976d2">单路排序的详细过程:</font></b>
1)从索引name找到第一个满足 name = ‘zhuge’ 条件的主键 id
2)根据主键 id 取出整行,<b><font color="#000000">取出</font><font color="#f57c00">所有字段的值</font><font color="#000000">,存入 sort_buffer 中</font></b>
3)从索引name找到下一个满足 name = ‘zhuge’ 条件的主键 id<br>
4) 重复步骤 2、3 直到 name != ‘zhuge’
5)对 sort_buffer 中的数据按照字段 position 进行排序
6)返回结果给客户端
<b><font color="#1976d2">双路排序的详细过程:</font></b>
1)从索引 name 找到第一个满足 name = ‘zhuge’ 的主键id
2)根据主键 id 取出整行,<b><font color="#000000">把</font><font color="#f57c00">排序字段 position</font><font color="#000000"> 和</font><font color="#f57c00">主键 id</font><font color="#000000"> 这两个字段放到 sort buffer</font></b> 中
3)从索引 name 取下一个满足 name = ‘zhuge’ 记录的主键 id
4)重复 3、4 直到不满足 name = ‘zhuge’
5)对 sort_buffer 中的字段 position 和主键 id 按照字段 position 进行排序
6)遍历排序好的 id 和字段 position,<b><font color="#f57c00">按照 id 的值回到原表中取出所有字段的值</font></b>返回给客户端
<b><font color="#1976d2">单路排序与双路排序对比:</font></b>
<b>单路排序</b>:会把所有需要查询的字段都放到 sort buffer 中进行排序
<b>双路排序</b>:只会把主键和需要排序的字段放到 sort buffer 中进行排序,再通过主键回到原表查询需要的字段
如果 MySQL <b>排序内存 sort_buffer 配置的比较小</b>并且<b>没有条件继续增加</b>了
可以适当把max_length_for_sort_data 配置小点
让优化器选择使用<b>双路排序</b>算法
可以在sort_buffer 中一次排序更多的行
只是需要再根据主键回到原表取数据
如果 MySQL <b>排序内存有条件可以配置比较大</b>
可以适当增大 max_length_for_sort_data 的值
让优化器优先选择全字段排序(<b>单路排序</b>)
把需要的字段放到 sort_buffer 中,这样排序后就会直接从内存里返回查询结果<br>
所以,MySQL<b>通过 max_length_for_sort_data 这个参数来控制排序</b>,在<b>不同场景使用不同的排序模式</b>,<b>从而提升排序效率</b>
<font color="#e57373"><b>注意,如果全部使用sort_buffer内存排序一般情况下效率会高于磁盘文件排序,但不能因为这个就随便增大sort_buffer(默认1M),mysql很多参数设置都是做过优化的,不要轻易调整</b></font>
<b>索引设计原则</b>
<b><font color="#2196f3">1. 代码先行,索引后上</font></b>
一般应该等到<b><font color="#4caf50">主体业务功能开发完毕</font></b>,把涉及到的相关SQL都拿出来分析之后<b><font color="#4caf50">再建立索引</font></b><br>
<b><font color="#2196f3">2. 联合索引尽量覆盖条件</font></b>
<b><font color="#e57373">尽量少建单值索引</font><font color="#000000">,</font><font color="#f57c00">可以设计</font><font color="#4caf50">两三个联合索引</font></b><br>
<b><font color="#4caf50">每个联合索引</font><font color="#f57c00">都尽量去包含</font></b><font color="#4caf50"><b>SQL语句里的 where、order by、group by 的字段</b></font>
同时,<font color="#f57c00"><b><u>还要</u></b></font><b><font color="#f57c00">确保</font></b>这些<b><font color="#4caf50">联合索引的字段顺序</font></b><font color="#f57c00"><b>尽量</b></font><b><font color="#f57c00">满足</font><font color="#4caf50">SQL查询的最左前缀原则</font></b>
<font color="#2196f3">3. 不要在小基数字段上建立索引</font>
<b>索引基数:</b>是指这个字段在表里总共有多少个不同的值
如果<b><font color="#e57373">对小基数字段建立索引</font></b>的话,<b><font color="#e57373">查找效率还不如全表扫描高</font>。</b>因为你的<b><font color="#e57373">索引树里就包含男和女两种值</font></b>,根本<b><font color="#e57373">没法进行快速的二分查找</font></b>,那用<font color="#e57373"><b>索引</b></font>就<font color="#e57373"><b>没有太大的意义</b></font>了
建议<b><font color="#4caf50">尽量使用那些基数比较大的字段</font></b><font color="#9e9e9e">(就是值比较多的字段)</font><b style=""><font color="#4caf50">建立索引</font></b>,这样<b><font color="#4caf50">才能发挥出B+树快速二分查找的优势</font></b>
<font color="#2196f3">4. 长字符串我们可以采用前缀索引</font>
<b>1)<font color="#4caf50">尽量对字段类型较小的列设计索引</font>,</b>比如说什么tinyint之类的。因为<font color="#4caf50"><b>字段类型较小</b></font>的话,<font color="#4caf50"><b>占用磁盘空间比较小</b></font>,<font color="#4caf50"><b>查找性能也会比较好</b></font>一点
<b>2)<font color="#f57c00">针对字段类型较大的列建立索引</font></b>,比如说varchar(255)之类的。因为<b style=""><font color="#e57373">字段类型较大</font></b>的话,可能<b><font color="#e57373">会比较占用磁盘空间</font></b>,<b><font color="#e57373">查找性能也会比较差</font></b>一点<font color="#e57373"><b></b></font>
<b><font color="#4caf50">可以稍微优化</font></b>下,<b><font color="#4caf50">对这个字段的前20个字符建立索引</font></b><font color="#9e9e9e">(就是说,将这个字段里每个值的前20个字符放在索引树里)</font>,类似于<b><font color="#4caf50"> KEY index(name(20), age, position)</font></b>
但是,<b><font color="#e57373">如果应用于order by name</font></b>,因<b><font color="#e57373">索引树仅包含前20个字符</font></b>,所以<b><font color="#e57373">排序无法使用索引,group by 同理</font></b>
<font color="#2196f3">5. where与order by冲突时,优先where</font>
<b><font color="#f57c00">在where和order by出现索引设计冲突时,到底是针对where去设计索引,还是针对order by设计索引?</font></b>
一般建议,让<b><font color="#4caf50">where条件去使用索引</font></b>来快速筛选出来一部分指定的数据,接着<b><font color="#4caf50">再进行排序</font></b>
因为<b><font color="#f57c00">大多数情况</font></b>,<b><font color="#4caf50">基于索引进行where筛选</font></b>往往<b><font color="#4caf50">可以最快速度筛选出</font></b>你要的数据<b><font color="#4caf50">且数据量不对太大</font></b>,然后<b><font color="#f57c00">做排序的成本</font></b>可能会<b><font color="#4caf50">小很多</font></b>
<font color="#2196f3">6. 基于慢sql查询做优化</font>
可以根据<b>监控后台的一些慢SQL</b>,<b><font color="#4caf50">针对这些慢sql查询做特定的索引优化</font></b><br>
<b>关于慢SQL查询可以参考这篇文章</b>:<a class="link" target="_bank" href="https://blog.csdn.net/qq_40884473/article/details/89455740">https://blog.csdn.net/qq_40884473/article/details/89455740</a>
<b>索引设计实战</b>
以社交场景APP来举例:
1. 一般搜索一些好友,用户数据量大,就需要对用户信息进行筛选及排序:<font color="#9e9e9e">(先不考虑分库分表情况)</font>
地区(省市)
性别
年龄
身高
爱好
评分
...
2. 对于后台程序,除了过滤以上条件,还可能需要分页处理,如:
select xx from user where xx=xx and xx=xx order by xx limit xx,xx
3. 这种情况如何合理设计索引呢?是否应该设计一个联合索引呢?
从筛选条件可看出,这些字段基数都不大
但应该给这些字段设计联合索引<font color="#4caf50"><b>(province,city,sex)</b></font>,因为这些信息字段查询太频繁了
4. 假如,新增需求1:用户需要进行年龄范围的筛选:
根据综上所学,这种范围查找的条件一般都要放在最后 <b style=""><font color="#4caf50">(province,city,sex,</font><i style=""><font color="#f57c00">age</font></i><font color="#4caf50">)</font></b>,因为联合索引范围之后条件是不能用索引的
但是当前我用不到sex性别筛选,会影响后边范围索引的使用,该怎么优化呢?
可以这么来优化,因为sex的字段基数比较小,我们在查询时包含它所有的值,如下写法:
where province=xx and city=xx and <b><font color="#4caf50">sex in ('female','male') </font></b>and age>=xx and age<=xx
对于爱好之类的字段也可以类似于sex字段的处理<b style=""><font color="#4caf50">(province,city,sex,</font><font color="#f57c00">hobby</font><font color="#4caf50">,age)</font></b>
4. 假如,新增需求2:用户需要筛选最近一周登录过的用户,希望能与活跃用户交友:
后台SQL可能这样写:where province=xx and city=xx and sex in ('female','male') and age>=xx and age<=xx and <font color="#f57c00"><b>latest_login_time>= xx</b></font>
但显然,latest_login_time 它不能使用索引,其实这里可以通过新增一个最近7天登录的标识字段
如果一周内登录,值就为1,否则为0
于是我们就可以把索引设计成<font color="#4caf50" style="font-weight: bold;">(province,city,sex,hobby,</font><font color="#f57c00" style="font-weight: bold;">is_login_in_latest_7_days</font><b style="color: rgb(76, 175, 80);">,age)</b><font color="#000000">,解决以上不能使用索引问题</font>
5. 实际应用场景中:
1) 一般情况下,通过这么多字段的索引过滤后的数据量不会太多,接下来再进行order by语句排序、limit 进行分页,性能还是比较高的
2) 特殊情况下,用户只筛选sex,如:where sex = 'female' orderby score limit xx,xx,那么上边的索引就用不上了,也不能把所有字段类似上班sex的方式用in语句拼接到SQL里。该怎么办呢?
其实我们可以<b><font color="#4caf50">再设计一个辅助的联合索引</font></b>,如:<b><font color="#4caf50"> (sex,score)</font></b>,这样就能满足要求了
<b><font color="#2196f3">综上所述,核心思想就是:</font></b>
<b>1. </b>尽量<b><font color="#4caf50">利用一两个复杂的多字段联合索引</font></b>,<font color="#4caf50"><b>抗下你80%以上的查询</b></font><br>
<b>2. </b>然后<b><font color="#4caf50">用一两个辅助索引尽量抗下剩余的一些非典型查询</font></b>
<font color="#4caf50"><b>保证</b></font>这种<b>大数据量表的查询</b>,<b>尽可能多的都能充分利用索引</b>,这样<b>就能保证查询速度和性能</b>了
0 条评论
下一页