zoukankan      html  css  js  c++  java
  • 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种翻车场景.
    */
    
    “fool me once,shame on you. fool me twice, shame on me.”,翻译过来的意思是“愚弄我一次,是你坏;愚弄我两次,是我蠢”。
  • 相关阅读:
    2016第50周五
    2016第50周四
    2016第50周三
    2016第50周二
    2016第50周一
    2016第49周日
    软件架构、框架、模式、模块、组件、插件概念汇总
    2016第49周五
    2016第49周四
    从服务器上共享文件上下载文件或上传文件
  • 原文地址:https://www.cnblogs.com/newcapecjmc/p/14100983.html
Copyright © 2011-2022 走看看