MySQL缓存失效的几种常见写法

转自:https://mp.weixin.qq.com/s/kP1WZSb9fFvP4KmlIyoV0w

导致MySQL索引失效的几种常见写法

最近一直忙着处理原来老项目遗留的一些SQL优化问题,由于当初表的设计以及字段设计的问题,随着业务的增长,出现了大量的慢SQL,导致MySQL的CPU资源飙升,基于此,给大家简单分享下这些比较使用的易于学习和使用的经验。
这次的话简单说下如何防止你的索引失效。
再说之前我先根据我最近的经验说下我对索引的看法,我觉得并不是所以的表都需要去建立索引,对于一些业务数据,可能量比较大了,查询数据已经有了一点压力,那么最简单、快速的办法就是建立合适的索引,但是有些业务可能表里就没多少数据,或者表的使用频率非常不高的情况下是没必要必须要去做索引的。就像我们有些表,2年了可能就10来条数据,有索引和没索引性能方面差不多多少。
索引只是我们优化业务的一种方式,千万为了为了建索引而去建索引。
下面是我此次测试使用的一张表结构以及一些测试数据

CREATE TABLE `user` (
  `id` int(5) unsigned NOT NULL AUTO_INCREMENT,
  `create_time` datetime NOT NULL,
  `name` varchar(5) NOT NULL,
  `age` tinyint(2) unsigned zerofill NOT NULL,
  `sex` char(1) NOT NULL,
  `mobile` char(12) NOT NULL DEFAULT '',
  `address` char(120) DEFAULT NULL,
  `height` varchar(10) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_createtime` (`create_time`) USING BTREE,
  KEY `idx_name_age_sex` (`name`,`sex`,`age`) USING BTREE,
  KEY `idx_ height` (`height`) USING BTREE,
  KEY `idx_address` (`address`) USING BTREE,
  KEY `idx_age` (`age`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=261 DEFAULT CHARSET=utf8;

INSERT INTO `bingfeng`.`user`(`id`, `create_time`, `name`, `age`, `sex`, `mobile`, `address`, `height`) VALUES (1, '2019-09-02 10:17:47', '冰峰', 22, '男', '1', '陕西省咸阳市彬县', '175');
INSERT INTO `bingfeng`.`user`(`id`, `create_time`, `name`, `age`, `sex`, `mobile`, `address`, `height`) VALUES (2, '2020-09-02 10:17:47', '松子', 13, '女', '1', NULL, '180');
INSERT INTO `bingfeng`.`user`(`id`, `create_time`, `name`, `age`, `sex`, `mobile`, `address`, `height`) VALUES (3, '2020-09-02 10:17:48', '蚕豆', 20, '女', '1', NULL, '180');
INSERT INTO `bingfeng`.`user`(`id`, `create_time`, `name`, `age`, `sex`, `mobile`, `address`, `height`) VALUES (4, '2020-09-02 10:17:47', '冰峰', 20, '男', '17765010977', '陕西省西安市', '155');
INSERT INTO `bingfeng`.`user`(`id`, `create_time`, `name`, `age`, `sex`, `mobile`, `address`, `height`) VALUES (255, '2020-09-02 10:17:47', '竹笋', 22, '男', '我测试下可以储存几个中文', NULL, '180');
INSERT INTO `bingfeng`.`user`(`id`, `create_time`, `name`, `age`, `sex`, `mobile`, `address`, `height`) VALUES (256, '2020-09-03 10:17:47', '冰峰', 21, '女', '', NULL, '167');
INSERT INTO `bingfeng`.`user`(`id`, `create_time`, `name`, `age`, `sex`, `mobile`, `address`, `height`) VALUES (257, '2020-09-02 10:17:47', '小红', 20, '', '', NULL, '180');
INSERT INTO `bingfeng`.`user`(`id`, `create_time`, `name`, `age`, `sex`, `mobile`, `address`, `height`) VALUES (258, '2020-09-02 10:17:47', '小鹏', 20, '', '', NULL, '188');
INSERT INTO `bingfeng`.`user`(`id`, `create_time`, `name`, `age`, `sex`, `mobile`, `address`, `height`) VALUES (259, '2020-09-02 10:17:47', '张三', 20, '', '', NULL, '180');
INSERT INTO `bingfeng`.`user`(`id`, `create_time`, `name`, `age`, `sex`, `mobile`, `address`, `height`) VALUES (260, '2020-09-02 10:17:47', '李四', 22, '', '', NULL, '165');


SELECT * FROM `user` WHERE `name` != '冰峰';

SELECT * FROM `user` WHERE height= 175;

SELECT * FROM `user` WHERE DATE(create_time) = '2020-09-03';

SELECT * FROM `user` WHERE age - 1 = 20;

SELECT * FROM `user` WHERE `name` = '张三' OR height = '175';

SELECT * FROM `user` WHERE `name` LIKE '%冰';

SELECT s.* FROM `user` s WHERE NOT EXISTS (SELECT * FROM `user` u WHERE u.name = s.`name` AND u.`name` = '冰峰')
SELECT * FROM `user` WHERE `name` NOT IN ('冰峰');

SELECT * FROM `user` WHERE address IS NULL
SELECT * FROM `user` WHERE address IS NOT NULL;

EXPLAIN SELECT * FROM `user` WHERE sex = '男';
EXPLAIN SELECT * FROM `user` WHERE name = '冰峰' AND sex = '男';

SELECT * FROM `user` WHERE sex = '男' AND age = 22 AND `name` = '冰峰';

SELECT * FROM `user` WHERE sex = '男' AND `name` != '冰峰' AND age = 22;

Mysql 索引失效的几种常见写法

https://mp.weixin.qq.com/s/NGYnMz0JULILhM0iPgdVTw

/*
参考链接:https://mp.weixin.qq.com/s/NGYnMz0JULILhM0iPgdVTw
为方便演示,我们提前先建立一张数据库表。新建一个用户表,id 为主键,user_id 为唯一索引,name 为普通索引,address 为普通索引:
**/

CREATE TABLE `t_user` (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `age` int DEFAULT NULL,
  `address` varchar(255) DEFAULT NULL,
  `user_id` int DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `index_user_id` (`user_id`) USING BTREE,
  KEY `index_name` (`name`) USING BTREE,
  KEY `index_address` (`address`) USING BTREE
) ENGINE=InnoDB;


/*
* 一、查询条件包含or,可能导致索引失效
*/

/*
1.执行一条简单SQL
**/
EXPLAIN 
select * from t_user where user_id=01;/*查看执行计划很显然走了索引*/ 

/*
2.加一个or条件,把age字段加上
*/
EXPLAIN 
SELECT * FROM t_user WHERE user_id =01 or age =18;/*很显然走了全表扫描*/ 

/*
* 分析结论:
(1)对于条件中出现or的情况,user_id列加了索引,age列是没有加索引的,
假设mysql一定走索引,可能需要三步:索引扫描+全表扫描+合并;
(2)如果它一开始就走全表扫描,直接一遍扫描就完事了;
(3)mysql是有优化器的,处于效率与成本,遇到or条件,索引可能失效,看起来也是合情合理的。

重点: 上面说的这种情况索引可能失效,没有说一定会失效;如果or条件的列都加了索引,索引可能会走的。
*/

/*
* 二、like通配符可能导致索引失效
*/

/*
*在业务中我们喜欢用模糊搜索的方式去做查询,举个例子,我们想搜索名称为 leijun:
*/
EXPLAIN
SELECT * FROM t_user WHERE name ='%leijun%';/*备注: 博文上说的:查看执行计划发现是全表扫描,说明索引失效了,但是我本机执行时没有失效,不知为何*/


/*试着把前面的%去掉,再查一遍*/
EXPLAIN
SELECT * FROM t_user WHERE name ='leijun%';/*正常走索引了*/


/*我们再把%加回来,这次不查询所有的字段,只查询id和name**/
EXPLAIN 
SELECT id,name from t_user where name ='%leijun%'

/*惊奇发现居然又走索引了,这是因为覆盖索引的原因*/

/*
* 分析结论:like查询以%开头,会导致索引失效。可以有两种方式优化:
  (1)使用覆盖索引
	(2)把%放后面
	重点:索引包含所有满足查询需要的数据的索引,称为“覆盖索引”.
*
**/

/*
*三、字符串列使用where一定用引号括起来,否则索引失效
**/

/* 从上面的建表语句中知道name列是varchar可变字符类型,如果我们在查询时忘了加引号会发生什么呢?*/

/*做一个简单的查询,此时:name列没有加引号:*/
EXPLAIN 
SELECT * FROM t_user WHERE name =123; /*name列是建了普通索引的,理论上应该走索引的,实际上全表扫描*/

/*把引号加起来*/
EXPLAIN 
SELECT * FROM t_user WHERE name ='123';/*这次乖乖走索引了*/

/*
* 分析结论:如果某一列是字符类型,但是使用where不加单引号时,是字符串跟数字的比较,
他们的类型不匹配,Mysqsl会做隐式的类型转换,把它们转换为浮点数再做比较.
*/

/*
*四、在索引列上使用mysql内置函数,索引失效
**/

/*
* name 列已经加了普通索引,但是在查询的时候给加了内置函数,作用是将name列全变成大写
*/

EXPLAIN 
SELECT * FROM t_user WHERE UPPER(name) ='LeiJun'; /*索引直接GG了,套了一层壳子,mysql直接全表扫描了*/

/*
*五、索引字段上适用is null或is not null 可能导致索引失效
**/
/*
根据前面的建表语句,name列和address列都加了普通索引
*/

/*
(1)查询name不为空的所有列
**/
EXPLAIN 
SELECT * FROM t_user where name is not null;/*正常走了索引*/

/*
(2)查询address不为空的所有列
**/
EXPLAIN 
SELECT * FROM t_user where address is not null;/*正常走了索引*/

/*
(3)查询name 不为空或者address不为空
**/
EXPLAIN 
SELECT * FROM t_user where name is not null or address is not null;/*索引失效了*/

/*
* 六、对索引列运算索引失效
*/
/*
对索引进行四则运算(如: + - * /),索引会失效
**/
/* user_id是索引列,在查询时进行 +1*/
EXPLAIN 
SELECT * FROM t_user where user_id +1 = 8;/*索引直接迷路了*/


/*
*七、联合索引ABC问题导致索引失效
*/
/*
* 重新建一张表,id是主键,name和age列都加了一个联合索引
*/
CREATE TABLE `t_user1` (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `age` int DEFAULT NULL,
  `address` varchar(255) DEFAULT NULL,
  `user_id` int DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `index_name_age` (`name`,`age`) USING BTREE
) ENGINE=InnoDB;

/*
* 特别要注意一下联合索引的顺序,name 在前,age在后
*/

/*
(1) where 条件同时满足name和age
*/

EXPLAIN  
SELECT * FROM t_user1 where name ='张三' and age =18;/*肯定走了索引*/

/*
(2) where 条件只有name列
*/
EXPLAIN  
SELECT * FROM t_user1 where name ='张三'; /**依然走了索引*/


/*
(3) where 条件只有name列
*/
EXPLAIN  
SELECT * FROM t_user1 where age =18; /**我们认为会走索引,实际上没走索引,翻车了*/

/*
* 分析结论:
1. 当我们创建一个联合索引的时候,如(kl,k2,k3) 相当于创建了(K1),(K1,K2)
和(k1,k2,k3)三个索引,这就是最左匹配原则。
2. 联合索引不满足最左原则,索引一般会失效,但是这个跟Mysql优化器有关。
*/

/*
* 总结:以上就是使用索引时最常见的7种翻车场景.
*/
posted @ 2021-12-10 14:22  朕在coding  阅读(369)  评论(0编辑  收藏  举报