3、MySQL 索引失效的场景
看表的行数、看下索引列的 cardinality 值,card 值只能直观反映 = 操作符返回的行数。
对于>=、<=、like、between and 的情况,card 值不能直观判断返回值的数据量。
有时候可以尝试着执行一下,但要注意,不是执行真正的 SQL,而是主要是为了得到 where 访问条件返回的行数,所以可以使用下面的技巧实现需求的转换:
select x.c_id,s.s_name from xuanke x join student s on x.stu_id=s.stu_id where s.s_name like ‘abc%’; ——————>
select count() from student where s_name like ‘abc%’;
上面的 sql 是不是原始 SQL,而是你想得到某个结果而自己写的 SQL。
如何查找失效索引?
如果索引失效,重建索引!
MySQL 中目前没法查看索引的状态信息。
Use information_schema,show tables,有 innodb_sys_indexs 系统所有的索引信息,
但索引状态信息实际没法看,show index from 语法其实访问的是
information_schema.STATISTICS 数据字典。
比如在列上有函数:MySQL 中不支持,Oracle 中支持在列上建立函数索引
select ... from t1 where upper(name) like 'ABC%';
比如在 where 条件中存在运算:
select ... from t1 where id-<;
select 尽量走索引,对于 dml 一定要走索引(否则就是全表锁,导致业务串行化)
使用了 hints,强制忽略了某个索引,导致没走索引
统计信息可能会出现严重不真实导致不走索引:表中有 万,索引唯一值有 万,但是旧统计信息中唯一值的数量才 ,导致不走索引,走全表扫描。
如何判断统计信息是否真实:
show table status like 't1';
show index from t1; 手工收集统计信息:
analyze table t1; 例如
对一个表做了 truncate 以后,系统在随后的时间里面,启动了一个自动收集统计信息的作业,这个表的行数更新变成 行。随后,对这个表进行数据的
导入,导入 万行,这时候 MySQL 不会去看表中真实的行数,还是会看统计信息的 行,这时候会出现问题,需要手工收集统计信息。
但是对于递增式的、每日规律变化的情况,统计信息没有必要每日收集。 对于统计信息的收集:
学会使用 UE、notepad++等工具批量手工收集统计信息:
Analyze table table_name;
利用 select concat(‘analyze table ',TABLE_NAME,';') from tables where table_schema=’tpcc1000’;
复制所有的表,利用 UE 编辑器的列编辑模式,直接去掉不必要的列,加上 analyze table 和分号变成语句,直接在 MySQL 里执行就好了。 举例:
MySQL> select concat('analyze table ',TABLE_NAME,';') from information_schema.tables where table_schema='TENNIS';
+-----------------------------------------+
| concat('analyze table ',TABLE_NAME,';') |
+-----------------------------------------+
| analyze table COMMITTEE_MEMBERS; |
| analyze table MATCHES; |
| analyze table PENALTIES; |
| analyze table PLAYERS; |
| analyze table TEAMS; | 在 ultraedit 里使用列模式,直接编辑,编辑完直接复制粘贴到 MySQL 里执行就行。
analynize table COMMITTEE_MEMBERS ;
analynize table MATCHES ;
analynize table PENALTIES ;
analynize table PLAYERS ;
analynize table TEAMS ; MySQL 自动收集统计信息的参数:
比如在 show table status like ‘customer’;的时候会自动收集,最好是手工收集。
、自动存储参数:innodb_stats_persistent
、变化量大的情况下,自动收集参数:innodb_stats_auto_recalc:
MySQL> show variables like '%stat%';
| innodb_stats_sample_pages | — —不管你访问多少,每次都是随机扫 个页,看看里面有多少行。
| innodb_stats_persistent_sample_pages | ——
analyze 是采样 个页,一般可以设置成 个页,所以 analyze 准确一些,建议定期手工收集一下。 | innodb_stats_persistent | ON ——
收集完统计信息以后,把收集的信息永久保存到数据字典里面去,数据库重新启动的时候这个统计信息还在,还可以继续使用,如果是 off,存到内存里面去,下次启动就没了,所以这个参数一定要是 on。
| innodb_stats_auto_recalc | ON — —当 update 或 delete 等操作产生大的影响时,如果这个参数是 on,会触发统计信息的自动收集。例如:变化超过 %就触发自动收集,有时候会关闭。 统计信息:
、表的行数
、索引列的唯一值的数量 关于统计信息需要知道:
、只有在统计的时候,才会更新对应的数据
、统计信息使用来生成执行计划的
、统计信息没有必要和表、索引保持实时更新
比如:一个表行数是 万,索引列唯一值的数量是 万,走索引效果很好;如果这个表的行数变成了 万,索引列唯一值的数量变成了 40万,不影响走索引的效果,所以一般在对表做 dml 时不会主动更新统计信息,因为这样会加重系统的负担。 、统计信息总是近似的反应表和索引的信息
如何手工修改表的行数以及 cardinality 值:为了欺骗 MySQL 是否走索引
需要注意:在执行 show table status like 语句时会自动收集统计信息。 、根据 mysql.innodb_table_stats 数据字典修改 n_rows 值:
[root@localhost][mysql]> select * from mysql.innodb_table_stats limit ;
+---------------+-------------------+---------------------+--------+----------------------+--------------------------+
| database_name | table_name | last_update | n_rows |clustered_index_size | sum_of_other_index_sizes |
+---------------+-------------------+---------------------+--------+----------------------+--------------------------+
| TENNIS | COMMITTEE_MEMBERS | -- :: | | | |
+---------------+-------------------+---------------------+--------+----------------------+--------------------------+
row in set (0.00 sec)
[root@localhost][mysql]> 、根据 mysql.innodb_index_stats 数据字典修改 cardinality 值:
[root@localhost][tpcc1000]> select * from mysql.innodb_index_stats where database_name='tpcc1000' and table_name='customer' and stat_description='c_first';
+---------------+------------+------------+---------------------+--------------+------------+-------------+------------------+
| database_name | table_name | index_name | last_update | stat_name | stat_value | sample_size | stat_description |
+---------------+------------+------------+---------------------+--------------+------------+-------------+------------------+
| tpcc1000 | customer | id_first | -- :: | n_diff_pfx01 | | | c_first |
+---------------+------------+------------+---------------------+--------------+------------+-------------+------------------+
row in set (0.00 sec)
[root@localhost][tpcc1000]> update mysql.innodb_index_stats set stat_value= where database_name='tpcc1000' and table_name='customer' and stat_description='c_first';
Query OK, row affected (0.01 sec)
Rows matched: Changed: Warnings: [root@localhost][tpcc1000]> select * from mysql.innodb_index_stats where database_name='tpcc1000' and table_name='customer' and stat_description='c_first';
+---------------+------------+------------+---------------------+--------------+------------+-------------+------------------+
| database_name | table_name | index_name | last_update | stat_name | stat_value | sample_size | stat_description |
+---------------+------------+------------+---------------------+--------------+------------+-------------+------------------+
| tpcc1000 | customer | id_first | -- :: | n_diff_pfx01 | | | c_first |
+---------------+------------+------------+---------------------+--------------+------------+-------------+------------------+
row in set (0.01 sec) [root@localhost][tpcc1000]>
mysql> select * from xuanke where c_id >;
Empty set (0.31 sec)
mysql> explain select * from xuanke where c_id >;
+----+-------------+--------+-------+-------------------+-------------------+---------+------+------+-----------------------+
| id | select_type | table | type | possible_keys | key |key_len | ref | rows | Extra |
+----+-------------+--------+-------+-------------------+-------------------+---------+------+------+-----------------------+
| | SIMPLE | xuanke | range | FK_Relationship_3 | FK_Relationship_3 | | NULL | | Using index condition |
+----+-------------+--------+-------+-------------------+-------------------+---------+------+------+-----------------------+
row in set (0.00 sec) mysql> explain select * from xuanke where c_id >;
+----+-------------+--------+------+-------------------+------+---------+------+--------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows |Extra |
+----+-------------+--------+------+-------------------+------+---------+------+--------+-------------+
| | SIMPLE | xuanke | ALL | FK_Relationship_3 | NULL | NULL |NULL | | Using where |
+----+-------------+--------+------+-------------------+------+---------+------+--------+-------------+
row in set (0.38 sec)
mysql>
例如状态值的列,有可能出现没有走索引的情况:
select * from dingdan where dingdanzhuangtai='未处理';
如何解决行数据倾斜:
、使用手工修改统计信息,card 值提升一下
、使用 like 的时候,会临时性使用采样的方式,从表中取 个数据块,统计“未处理”值的数量,这时候反而准确了。
、使用 force index 和 ignore index 来做特殊处理,在 Oracle 中不会出现数据倾斜导致不走索引的情况,因为有数据值所占百分比,能够正确引导是否走索引。
根本原因还是从表中访问的行数过多
针对 like、<=、>=、between and 等不确定的一些条件,会进行动态采样,可能出现有时候走索引,有时候不走索引的情况。
数据库最核心的组件是优化器,对 SQL 进行解析,生成执行计划。 优化器工作模式:
、RBO(rule based optimization),基于规则的优化器,条件太苛刻,现在基本不用
主要干什么:
、是否走索引(定义规则为:是否有 where 条件、where 条件是否有索引等,如果满足规则就走索引,不满足就不走索引)
、表的连接顺序等(定义规则为:按照写的 SQL 中的表连接顺序)针对这种优化器,我们在写 SQL 的时候,就需要了解这种优化器的工作习性(规则库),按照他的脾气来,强烈依赖 SQL 的写法。 、CBO(cost based optimization),基于成本的优化器(现在主要是 CBO,MySQL只有 CBO)
、在解析以前,会做一件事情,对 SQL 进行改写,改写成更合理的一些 SQL语句
、将执行路径列出来,计算每一个执行路径的成本(cpu 和 io 的成本,主要是 io 成本),基于统计信息进行计算,估算一个成本
、选择执行成本最低的 SQL 作为执行计划 对于 CBO:
、不过度依赖 SQL 写法
、严重依赖统计信息
、数字列不害怕类型转换
、字符串列非常害怕隐式类型转换,因此对于字符串的列,一定要加上'':
字符串列“坑”
我们习惯于将很多列定位为字符串,例如手机号列,在 where 的时候,也习惯与=,不加'',因为我们认为这是数字,但是定义的是字符列,发生隐式转换,导致索引失效。
、日期列不害怕隐式类型转换
设计原则:
如果存储的是数字,就定义成数字列
如果存储的是日期,就定义成日期列
如果存储的是字符串,where 条件的时候,右面一定要加上''
因为数据库认为等于的时候会取少量数据,认为不等于会取大量的数据
where like '%abc',有索引也会失效,后面的中这种写法索引可能不会失效
select * from ... where name like 'abc%'
因为认为 not in 时结果集会比较大,而 in 的时候结果集会比较小。
对 not in、not exists 和 left join 之间的相互转换,索引都生效了:
mysql> explain select * from student s where s.stu_id not in (select stu_id from xuanke);
+----+-------------+--------+-------+-------------------+-------------------+---------+------ +--------+-------------+
| id | select_type | table | type | possible_keys | key |key_len | ref | rows | Extra |
+----+-------------+--------+-------+-------------------+-------------------+---------+------ +--------+-------------+
| | PRIMARY | s | ALL | NULL | NULL | NULL | NULL | | Using where |
| | SUBQUERY | xuanke | index | FK_Relationship_1 |FK_Relationship_1 | | NULL | | Using index |
+----+-------------+--------+-------+-------------------+-------------------+---------+------ +--------+-------------+
rows in set (0.00 sec)
mysql> explain select * from student s where not exists (select from xuanke x where x.stu_id=s.stu_id);
+----+--------------------+-------+------+-------------------+-------------------+---------+-----------------+--------+-------------+
| id | select_type | table | type | possible_keys | key| key_len | ref | rows | Extra |
+----+--------------------+-------+------+-------------------+-------------------+---------+-----------------+--------+-------------+
| | PRIMARY | s | ALL | NULL | NULL | NULL | NULL | | Using where |
| | DEPENDENT SUBQUERY | x | ref | FK_Relationship_1 |FK_Relationship_1 | | xuanke.s.stu_id | | Using index |
+----+--------------------+-------+------+-------------------+-------------------+---------+-----------------+--------+-------------+
mysql> explain select * from student s left join xuanke x on s.stu_id=x.stu_id and x.c_id is null;
+----+-------------+-------+------+-------------------------------------+-------------------+---------+-----------------+--------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+-------------------------------------+-------------------+---------+-----------------+--------+-------------+
| | SIMPLE | s | ALL | NULL | NULL | NULL | NULL | | NULL|
| | SIMPLE | x | ref | FK_Relationship_1,FK_Relationship_3 |FK_Relationship_1 | | xuanke.s.stu_id | | Using where |
+----+-------------+-------+------+-------------------------------------+-------------------+---------+-----------------+--------+-------------+
rows in set (0.00 sec)
mysql> explain select * from student s where exists (select from xuanke x
where x.stu_id=s.stu_id);
+----+--------------------+-------+------+-------------------+-------------------+---------+-----------------+--------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------------+-------+------+-------------------+-------------------+---------+-----------------+--------+-------------+
| | PRIMARY | s | ALL | NULL | NULL | NULL | NULL | | Using where |
| | DEPENDENT SUBQUERY | x | ref | FK_Relationship_1 |FK_Relationship_1 | | xuanke.s.stu_id | | Using index |
+----+--------------------+-------+------+-------------------+-------------------+---------+-----------------+--------+-------------+
rows in set (0.00 sec)
mysql> explain select * from student s where s.stu_id in (select stu_id from xuanke);
+----+--------------+-------------+--------+-------------------+-------------------+--------- +-----------------+--------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------+-------------+--------+-------------------+-------------------+--------- +-----------------+--------+-------------+
| | SIMPLE | s | ALL | PRIMARY |NULL | NULL | NULL | | Using where |
| | SIMPLE | <subquery2> | eq_ref | <auto_key> |<auto_key> | | xuanke.s.stu_id | | NULL |
| | MATERIALIZED | xuanke | index | FK_Relationship_1 |FK_Relationship_1 | | NULL | | Using index |
+----+--------------+-------------+--------+-------------------+-------------------+--------- +-----------------+--------+-------------+
rows in set (0.00 sec)
发生了隐式类型转换,导致索引会失效:
explain select * from login_record1 where t_time=cast('2011-1-1' as date);
explain select * from login_record1 where d_date=cast('13:00:00' as time);
explain select * from login_record1 where t_time='2011-1-1'; //索引不会失效
is not null 有时候走索引,有时候不走索引,还是比较准确,主要看空值和非空值的数量。
is null 可能会成为一个坑。
MySQL 没有存储数据分布,因此在进行 where 条件的时候,可能会出现数据倾斜的盲点,反而采用 like 等模糊匹配的时候,因为会刺激 mysql 进行动态采样,反而会比较准确。有时候会出现下面的一些写法:
、like 'abc';来代替='abc'
、between and 来代替=
3、MySQL 索引失效的场景的更多相关文章
- MySQL索引失效的场景
WHERE字句的查询条件里有不等于号(WHERE column!=-),MYSQL将无法使用索引 类似地,如果WHERE字句的查询条件里使用了函数(如:WHERE DAY(column)=-),MYS ...
- MySQL索引失效的几种场景
我们都知道建立索引能够提高查询效率,那么是不是任何情况下都能提高呢,当然不是的的,下面我们就来列举一些常见的索引失效的场景. 借用上一篇文章的dm_person_info表 在card_code列没加 ...
- MySQL索引失效的常见场景
当然请记住,explain是一个好习惯! MySQL索引失效的常见场景 在验证下面的场景时,请准备足够多的数据量,因为数据量少时,MySQL的优化器有时会判定全表扫描无伤大雅,就不会命中索引了. 1. ...
- 面试突击60:什么情况会导致 MySQL 索引失效?
为了验证 MySQL 中哪些情况下会导致索引失效,我们可以借助 explain 执行计划来分析索引失效的具体场景. explain 使用如下,只需要在查询的 SQL 前面添加上 explain 关键字 ...
- MySQL 索引失效-模糊查询,最左匹配原则,OR条件等。
索引失效 介绍 索引失效就是我们明明在查询时的条件为索引列(包括自己新建的索引),但是索引不能起效,走的是全表扫描.explain 后可查看type=ALL. 这是为什么呢? 首先介绍有以下几种情况索 ...
- mysql索引总结(4)-MySQL索引失效的几种情况
mysql索引总结(1)-mysql 索引类型以及创建 mysql索引总结(2)-MySQL聚簇索引和非聚簇索引 mysql索引总结(3)-MySQL聚簇索引和非聚簇索引 mysql索引总结(4)-M ...
- 面试题: MySQL 索引失效的10大原因
个人博客网:https://wushaopei.github.io/ (你想要这里多有) 1.建表: CREATE TABLE staffs ( id INT PRIMARY KEY AUTO_ ...
- Mysql 索引失效场景
例如:一张USER表 有字段属性 name,age 其中name为索引 下面列举几个索引失效的情况 1. select * from USER where name=‘xzz’ or age= ...
- MySQL索引失效之隐式转换
常见索引失效: 1. 条件索引字段"不干净":函数操作.运算操作 2. 隐式类型转换:字符串转数值:其他类型转换 3. 隐式字符编码转换:按字符编码数据长度大的方向转换,避免数据截 ...
随机推荐
- 洛谷$P3756\ [CQOI2017]$老$C$的方块 网络流
正解:网络流 解题报告: 传送门$QwQ$ 看到不能出现给定的讨厌的图形,简单来说就,特殊边两侧的方格不能同时再连方格. 所以如果出现,就相当于是四种方案?就分别炸四个格子. 然后冷静分析一波之后发现 ...
- ObserverPattern(观察者模式)-----Java/.Net
当对象间存在一对多关系时,则使用观察者模式(Observer Pattern).比如,当一个对象被修改时,则会自动通知它的依赖对象.观察者模式属于行为型模式
- Java后台创建Socket服务接收硬件终端发送的数据
最近项目中有遇到后台接收硬件终端发送的数据并解析存储的需求,代码总结如下(有时间再来一一讲解,最近比较忙): @Override public void start() { ExecutorServi ...
- 「UVA12004」 Bubble Sort 解题报告
UVA12004 Bubble Sort Check the following code which counts the number of swaps of bubble sort. int f ...
- day2(使用list和tuple)
list list是一种有序的集合 >>>aaa = ['abc','bob','tracy'] >>>aaa ['abc','bob','tracy'] len( ...
- 从零开始学asyncio(上)
这篇文章主要是介绍生成器和IO多路复用机制, 算是学习asyncio需要的预备知识. 这个系列还有另外两篇文章: 从零开始学asyncio(中) 从零开始学asyncio(下) 一. 简单爬虫实例 首 ...
- Java工作流引擎系统节点接收人设置“其他方式总结”系列讲解
关键字: 驰骋工作流程快速开发平台 工作流程管理系统 工作流引擎 asp.net工作流引擎 java工作流引擎. 开发者表单 拖拽式表单 工作流系统CCBPM节点访问规则接收人规则 适配数据库: o ...
- Java 进程占用内存过多,幕后元凶原来是线程太多
那天中午吃饭,一个同事说,那个项目组的人快气死我了,程序有问题,早晨在群里@了他们,到中午才回消息,然后竟然还说他们的程序没有问题,是我们这边调用的太频繁了. 简直想笑. 背景说明 我们当前这个系统和 ...
- Spring Boot2 系列教程 (十三) | 整合 MyBatis (XML 版)
前言 如题,今天介绍 SpringBoot 与 Mybatis 的整合以及 Mybatis 的使用,之前介绍过了 SpringBoot 整合MyBatis 注解版的使用,上一篇介绍过 MyBatis ...
- 倍增LCA模板2董博文版 伪代码
Dfs(int rt){ f[][rt]; ;k<=;k++) f[k][rt]=f[k-][f[k-][rt]]; } int LCA(int x,int y){ if(Dp[x]<Dp ...