热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

UPDATE能走索引还会锁全表吗

导读执行UPDATE时,WEHRE条件列虽已有索引,但还会锁全表,肿么回事?问题描述叶师傅有次上课过程中执行UPDATE测试案例时,发现虽然WHERE条件列已有索

导读

执行UPDATE时,WEHRE条件列虽已有索引,但还会锁全表,肿么回事?

问题描述

叶师傅有次上课过程中执行UPDATE测试案例时,发现虽然WHERE条件列已有索引,有时候能利用二级索引进行更新(且只锁定相应必要的几行记录),但有时候却变成了根据主键进行更新,且会锁全表。我们先来看看下面的例子。

测试表 t1

CREATE TABLE `t1` (
 `c1` int(10) unsigned NOT NULL DEFAULT '0',
 `c2` int(10) unsigned NOT NULL DEFAULT '0',
 `c3` int(10) unsigned NOT NULL DEFAULT '0',
 `c4` int(10) unsigned NOT NULL DEFAULT '0',
 PRIMARY KEY (`c1`),
 KEY `c2` (`c2`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

表中数据

+----+----+----+----+
| c1 | c2 | c3 | c4 |
+----+----+----+----+
|  0 |  0 |  0 |  0 |
|  1 |  1 |  1 |  0 |
|  3 |  3 |  3 |  0 |
|  4 |  2 |  2 |  0 |
|  6 |  8 |  5 |  0 |
|  7 |  6 |  6 | 10 |
| 10 | 10 |  4 |  0 |
+----+----+----+----+

case1:根据二级索引UPDATE,不锁全表

  • 先看执行计划

yejr@imysql.com [yejr]>desc update t1 set c4=123 where c2>=8\G
*************************** 1. row ***************************
          id: 1
 select_type: UPDATE
       table: t1
  partitions: NULL
        type: range
possible_keys: c2
         key: c2
     key_len: 4
         ref: const
        rows: 2
    filtered: 100.00
       Extra: Using where
  • 启动两个session执行UPDATE测试

session1 session 2
mysql> begin; 
mysql> update t1 set c4=123 where c2>=8;

Query OK, 2 rows affected (0.00 sec)
Rows matched: 2 Changed: 2 Warnings: 0


mysql> begin;
mysql> select * from t1 where c2 = 7 for update;

...
1 row in set (0.00 sec)
#直接可得到结果,不会被阻塞

case2:根据PRIMARY KEY更新,锁全表

yejr@imysql.com [yejr]>desc update t1 set c4=123 where c2>=6\G
*************************** 1. row ***************************
          id: 1
 select_type: UPDATE
       table: t1
  partitions: NULL
        type: index
possible_keys: c2
         key: PRIMARY
     key_len: 4
         ref: NULL
        rows: 7
    filtered: 100.00
       Extra: Using where

我们能看到本次执行计划是 根据主键索引进行更新,且会锁全表

  • 同样地,启动两个session执行UPDATE测试

session1 session 2
mysql> begin; 
mysql> update t1 set c4=123 where c2>=6;

Query OK, 3 rows affected (0.00 sec)
Rows matched: 3 Changed: 3 Warnings: 0


mysql> begin;
mysql> select * from t1 where c2 = 3 for update;
#无法得到结果,被阻塞了
mysql> rollback; 
#执行rollback,释放锁


... 
1 row in set (4.23 sec) 
#session1释放锁后才能得到结果

查看行锁等待情况

yejr@imysql.com [yejr]>select * from sys.innodb_lock_waits\G
*************************** 1. row ***************************
               wait_started: 2017-08-15 15:20:20
                   wait_age: 00:00:17
              wait_age_secs: 17
               locked_table: `yejr`.`t1`
               locked_index: PRIMARY  <--主键上加锁
                locked_type: RECORD
             waiting_trx_id: 268350
        waiting_trx_started: 2017-08-15 15:20:20
            waiting_trx_age: 00:00:17
    waiting_trx_rows_locked: 2
  waiting_trx_rows_modified: 0
                waiting_pid: 13
              waiting_query: select * from t1 where c2 = 3 for update
            waiting_lock_id: 268350:387:3:4
          waiting_lock_mode: X
            blocking_trx_id: 268349
               blocking_pid: 12
             blocking_query:  NULL
           blocking_lock_id: 268349:387:3:4
         blocking_lock_mode: X
       blocking_trx_started: 2017-08-15 15:20:18
           blocking_trx_age: 00:00:19
   blocking_trx_rows_locked: 8  <-- 所有记录都被加锁了
 blocking_trx_rows_modified: 3  <---持有锁的事务更新了3行记录
    sql_kill_blocking_query: KILL QUERY 12
sql_kill_blocking_connection: KILL 12

问题分析

好了,案例说完了,也该说原因了。

肾好的同学可能记得我说过一个结论:当MySQL预估扫描行数超过全表总数约 20% ~ 30% 时,即便有二级索引,也会直接升级为全表扫描

这个结论的原因并不难理解,二级索引的顺序和主键顺序一般来说是不一样的,根据二级索引的顺序回表读数据时,实际上对于主键则很可能是随机扫描,因此当需要随机扫描的数量超过一定比例时(一般是20% ~ 30%),则优化器会决定直接改成全表扫描

上述说法出处:WHERE Clause Optimization(https://dev.mysql.com/doc/refman/5.7/en/where-optimization.html

Each table index is queried, and the best index is used unless the optimizer believes that it is more efficient to use a table scan. At one time, a scan was used based on whether the best index spanned more than 30% of the table, but a fixed percentage no longer determines the choice between using an index or a scan. The optimizer now is more complex and bases its estimate on additional factors such as table size, number of rows, and I/O block size.

不过,上面这个结论是针对读数据的情况,UPDATE/DELETE修改数据时是否也这样呢?

答案是肯定的,要不然上面的测试结果怎么解释......

按照官方开发者的说法,当优化器评估根据二级索引更新行数超过约50%(从上面测试结果来看,其实20% ~ 30%就已经是这样了,不过这个比例并不是固定值,会根据各种代价计算自动调整)就会改成走主键索引,并且锁全表,这么做是既定的策略,原因和上面一样,也是为了提高检索效率。

总结

老调重弹,几点建议:

  • 不管检索还是更新,都尽可能利用索引;

  • 不要一次性检索或更新大批量数据,建议分城多批次;

  • 事务尽快提交,降低行锁持有时间及其影响。

本文提到的问题,我也提了个BUG#87021,有兴趣的可以去看看。

延伸阅读

  • UTF8字符集的表怎么直接转UTF8MB4?

  • 细说ANALYZE TABLE

  • 优化系列 | DELETE子查询改写优化

  • [MySQL优化案例]系列 — 分页优化

  • 曝!和叶师傅**的正确姿势


知识无界限,不再加原创

喜欢就转走,铁粉加密圈

640?wx_fmt=png


好铁观音尽在

「老叶茶馆」

http://yejinrong.com

640?wx_fmt=png


推荐阅读
  • MySQL索引详解与优化
    本文深入探讨了MySQL中的索引机制,包括索引的基本概念、优势与劣势、分类及其实现原理,并详细介绍了索引的使用场景和优化技巧。通过具体示例,帮助读者更好地理解和应用索引以提升数据库性能。 ... [详细]
  • 深入理解Redis的数据结构与对象系统
    本文详细探讨了Redis中的数据结构和对象系统的实现,包括字符串、列表、集合、哈希表和有序集合等五种核心对象类型,以及它们所使用的底层数据结构。通过分析源码和相关文献,帮助读者更好地理解Redis的设计原理。 ... [详细]
  • Scala 实现 UTF-8 编码属性文件读取与克隆
    本文介绍如何使用 Scala 以 UTF-8 编码方式读取属性文件,并实现属性文件的克隆功能。通过这种方式,可以确保配置文件在多线程环境下的一致性和高效性。 ... [详细]
  • 本文详细介绍了 MySQL 中 LAST_INSERT_ID() 函数的使用方法及其工作原理,包括如何获取最后一个插入记录的自增 ID、多行插入时的行为以及在不同客户端环境下的表现。 ... [详细]
  • 优化局域网SSH连接延迟问题的解决方案
    本文介绍了解决局域网内SSH连接到服务器时出现长时间等待问题的方法。通过调整配置和优化网络设置,可以显著缩短SSH连接的时间。 ... [详细]
  • 使用Pandas高效读取SQL脚本中的数据
    本文详细介绍了如何利用Pandas直接读取和解析SQL脚本,提供了一种高效的数据处理方法。该方法适用于各种数据库导出的SQL脚本,并且能够显著提升数据导入的速度和效率。 ... [详细]
  • MySQL DateTime 类型数据处理及.0 尾数去除方法
    本文介绍如何在 MySQL 中处理 DateTime 类型的数据,并解决获取数据时出现的.0尾数问题。同时,探讨了不同场景下的解决方案,确保数据格式的一致性和准确性。 ... [详细]
  • 通过Web界面管理Linux日志的解决方案
    本指南介绍了一种利用rsyslog、MariaDB和LogAnalyzer搭建集中式日志管理平台的方法,使用户可以通过Web界面查看和分析Linux系统的日志记录。此方案不仅适用于服务器环境,还提供了详细的步骤来确保系统的稳定性和安全性。 ... [详细]
  • 在多线程编程环境中,线程之间共享全局变量可能导致数据竞争和不一致性。为了解决这一问题,Linux提供了线程局部存储(TLS),使每个线程可以拥有独立的变量副本,确保线程间的数据隔离与安全。 ... [详细]
  • 作者:守望者1028链接:https:www.nowcoder.comdiscuss55353来源:牛客网面试高频题:校招过程中参考过牛客诸位大佬的面经,但是具体哪一块是参考谁的我 ... [详细]
  • 本文深入探讨了Python中的高阶函数和Lambda表达式的使用方法,结合实际案例解析其应用场景,帮助开发者更好地理解和运用这些强大的工具。 ... [详细]
  • 本文探讨了高质量C/C++编程的最佳实践,并详细分析了常见的内存错误及其解决方案。通过深入理解内存管理和故障排除技巧,开发者可以编写更健壮的程序。 ... [详细]
  • 网易严选Java开发面试:MySQL索引深度解析
    本文详细记录了网易严选Java开发岗位的面试经验,特别针对MySQL索引相关的技术问题进行了深入探讨。通过本文,读者可以了解面试官常问的索引问题及其背后的原理。 ... [详细]
  • 深入理解T-SQL中的NULL与三值逻辑
    本文探讨了SQL Server中的三值逻辑,解释了谓词计算结果为TRUE、FALSE和UNKNOWN的规则。通过具体示例,详细说明了如何正确处理NULL值,并探讨了在不同约束条件下的行为。 ... [详细]
  • 华为USG基于源地址的多出口策略路由配置
    网络拓扑如下:组网情况:企业用户主要有技术部(VLAN10)和行政部(VLAN20),通过汇聚交换机连接到USG。企业分别通过两个不同运营商(ISP1和ISP2)连接到 ... [详细]
author-avatar
兴霖先玲广_215
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有