zoukankan      html  css  js  c++  java
  • 几个影响sql性能语句的例子

    几个影响sql语句性能的例子

    • 案例一:假设你现在维护了一个交易系统,其中交易记录表tradelog包含交易流水号(tradeid)、交易员id(operator)、交易时间(t_modified)等字段。为了便于描述,我们先忽略其他字段。这个表的建表语句如下:

      mysql> CREATE TABLE `tradelog` (
      `id` int(11) NOT NULL,
      `tradeid` varchar(32) DEFAULT NULL,
      `operator` int(11) DEFAULT NULL,
      `t_modified` datetime DEFAULT NULL,
      PRIMARY KEY (`id`),
      KEY `tradeid` (`tradeid`),
      KEY `t_modified` (`t_modified`)
      ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
      

      假设,现在已经记录了从2016年初到2018年底的所有数据,运营部门有一个需求是,要统计发生在所有年份中7月份的交易记录总数。这个逻辑看上去并不复杂,你的SQL语句可能会这么写:

      mysql> select count(*) from tradelog where month(t_modified)=7;
      

      由于t_modified字段上有索引,于是你就很放心地在生产库中执行了这条语句,但却发现执行了特别久,才返回了结果。

      如果对字段做了函数计算,就用不上索引了,这是MySQL的规定。

      可以再追问一句为什么?为什么条件是where t_modified='2018-7-1’的时候可以用上索引,而改成where month(t_modified)=7的时就不行了?

      对索引字段做函数操作,可能会破坏索引值的有序性,因此优化器就决定放弃走树搜
      索功能。

      需要注意的是,优化器并不是要放弃使用这个索引。在这个例子里,放弃了树搜索功能,优化器可以选择遍历主键索引,也可以选择遍历索引t_modified,优化器对比索引大小后发现,索引t_modified更小,遍历这个索引比遍历主键索引来得更快。因此最终还是会选择索引t_modified。

    Extra字段的Using index,表示的是使用了覆盖索引。也就是说,由于在t_modified字段加了month()函数操作,导致了全索引扫描。

    按照下面这个写法,优化器就能按照我们预期的,用上t_modified索引的快速定位能力了。

    mysql> select count(*) from tradelog where
    ‑> (t_modified >= '2016‑7‑1' and t_modified<'2016‑8‑1') or
    ‑> (t_modified >= '2017‑7‑1' and t_modified<'2017‑8‑1') or
    ‑> (t_modified >= '2018‑7‑1' and t_modified<'2018‑8‑1');
    
    • 案例二:隐式类型转换

      我们一起看一下这条SQL语句:

      mysql> select * from tradelog where tradeid=110717;
      

      交易编号tradeid这个字段上,本来就有索引,但是explain的结果却显示,这条语句需要走全表扫描。你可能也发现了,tradeid的字段类型是varchar(32),而输入的参数却是整型,所以需要做类型转换。

      • 数据类型转换的规则是什么

        • 在MySQL中,字符串和数字做比较的话,是将字符串转换成数字。
      • 为什么有数据类型转换,就要走全索引扫描。

        • 以上sql语句相当于

          mysql> select * from tradelog where CAST(tradid AS signed int) = 110717;
          
        • 也就是说,这条语句触发了我们上面说到的规则:对索引字段做函数操作,优化器会放弃走树搜索功能。

      • 同样的例子比如:,字符集utf8mb4是utf8的超集,所以当这两个类型的字
        符串在做比较的时候,MySQL内部的操作是,先把utf8字符串转成utf8mb4字符集,再做比较。

        • 这个设定很好理解,utf8mb4是utf8的超集。类似地,在程序设计语言里面,做自动类型转换的时候,为了避免数据在转换过程中由于截断导致数据错误,也都
          是“按数据长度增加的方向”进行转换的。

        • 也就是说,实际上这个语句等同于下面这个写法:

          select * from trade_detail where CONVERT(traideid USING utf8mb4)=$L2.tradeid.value;
          
        • CONVERT()函数,在这里的意思是把输入的字符串转成utf8mb4字符集。这就再次触发了我们上面说到的原则:对索引字段做函数操作,优化器会放弃走树搜索功能。

    • 小结:

      1. 对于给不同年份中的月份加索引,我们要指定年份才能走索引,因为不同年份可能会有相同的月份,mysql优化器不知如何选择,不会走指定字段索引。
      2. 保持字段类型一致,比如字段和数字,mysql会自动的调用函数将字符串转化为数字,会走全表扫描。
      3. 保持字符集一致,charset=utf8,如果跨表查询,两张表的字符集应该相同,否则mysql内部还是会调用函数,因此会走全表扫描。
      4. 在每次代码升级的时候我们都应该调用explain来看一下sql语句是否按索引来走的。
      5. 调用函数就不会走我们已经建好的索引字段。

    查一行的sql语句执行很慢

    • 第一类 查询长时间不返回

      • mysql> select * from t where id=1;
        
      • 执行show processlist命令,查看当前语句处于什么状态。

      • 等释放

        -

        • 可以看到这个语句目前的状态是waiting for table metadata lock,表示有一个线程正在表t上请求或者持有MDL写锁,把select语句堵住了
      • 等行锁

        • mysql> select * from t where id=1 lock in share mode;
          
        • 由于访问id=1这个记录时要加读锁,如果这时候已经有一个事务在这行记录上持有一个写锁,我们的select语句就会被堵住。

        • 显然,session A启动了事务,占有写锁,还不提交,是导致session B被堵住的原因。

    • 第二类:查询慢

      • mysql> select * from t where c=50000 limit 1;
        
        • 由于字段c上没有索引,这个语句只能走id主键顺序扫描,因此需要扫描5万行。
      • mysql> select * from t where id=1; # 假如这条语句慢查询日志返回的是800毫秒
        
        select * from t where id=1 lock in share mode # 这条语句返回的查询时间是0.2毫秒
        

        为什么结果相差会这么大呢?

        • session A先用start transaction with consistent snapshot命令启动了一个事务,之
          后session B才开始执行update 语句。

        • session B执行完100万次update语句后,id=1这一行处于什么状态呢?

        • session B更新完100万次,生成了100万个回滚日志(undo log)。

        • 带lock in share mode的SQL语句,是当前读,因此会直接读到1000001这个结果,所以速度很快;而select * from t where id=1这个语句,是一致性读,因此需要从1000001开始,依次执行undo log,执行了100万次以后,才将1这个结果返回。所以返回查询时间很长

  • 相关阅读:
    rsyslog
    java实现黄金分割数
    java实现黄金分割数
    java实现黄金分割数
    java实现黄金分割数
    java实现黄金分割数
    java实现低碳生活大奖赛
    java实现低碳生活大奖赛
    java实现低碳生活大奖赛
    java实现低碳生活大奖赛
  • 原文地址:https://www.cnblogs.com/jimmyhe/p/11105931.html
Copyright © 2011-2022 走看看