热门标签 | 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


推荐阅读
  • 深入解析 Spring Security 用户认证机制
    本文将详细介绍 Spring Security 中用户登录认证的核心流程,重点分析 AbstractAuthenticationProcessingFilter 和 AuthenticationManager 的工作原理。通过理解这些组件的实现,读者可以更好地掌握 Spring Security 的认证机制。 ... [详细]
  • 本文介绍如何在现有网络中部署基于Linux系统的透明防火墙(网桥模式),以实现灵活的时间段控制、流量限制等功能。通过详细的步骤和配置说明,确保内部网络的安全性和稳定性。 ... [详细]
  • 深入理解Redis的数据结构与对象系统
    本文详细探讨了Redis中的数据结构和对象系统的实现,包括字符串、列表、集合、哈希表和有序集合等五种核心对象类型,以及它们所使用的底层数据结构。通过分析源码和相关文献,帮助读者更好地理解Redis的设计原理。 ... [详细]
  • 解读MySQL查询执行计划的详细指南
    本文旨在帮助开发者和数据库管理员深入了解如何解读MySQL查询执行计划。通过详细的解析,您将掌握优化查询性能的关键技巧,了解各种访问类型和额外信息的含义。 ... [详细]
  • 扫描线三巨头 hdu1928hdu 1255  hdu 1542 [POJ 1151]
    学习链接:http:blog.csdn.netlwt36articledetails48908031学习扫描线主要学习的是一种扫描的思想,后期可以求解很 ... [详细]
  • 本文详细介绍了Java中org.w3c.dom.Text类的splitText()方法,通过多个代码示例展示了其实际应用。该方法用于将文本节点在指定位置拆分为两个节点,并保持在文档树中。 ... [详细]
  • 本文介绍如何通过创建替代插入触发器,使对视图的插入操作能够正确更新相关的基本表。涉及的表包括:飞机(Aircraft)、员工(Employee)和认证(Certification)。 ... [详细]
  • MySQL索引详解与优化
    本文深入探讨了MySQL中的索引机制,包括索引的基本概念、优势与劣势、分类及其实现原理,并详细介绍了索引的使用场景和优化技巧。通过具体示例,帮助读者更好地理解和应用索引以提升数据库性能。 ... [详细]
  • 基于KVM的SRIOV直通配置及性能测试
    SRIOV介绍、VF直通配置,以及包转发率性能测试小慢哥的原创文章,欢迎转载目录?1.SRIOV介绍?2.环境说明?3.开启SRIOV?4.生成VF?5.VF ... [详细]
  • 毕业设计:基于机器学习与深度学习的垃圾邮件(短信)分类算法实现
    本文详细介绍了如何使用机器学习和深度学习技术对垃圾邮件和短信进行分类。内容涵盖从数据集介绍、预处理、特征提取到模型训练与评估的完整流程,并提供了具体的代码示例和实验结果。 ... [详细]
  • 深入解析 Apache Shiro 安全框架架构
    本文详细介绍了 Apache Shiro,一个强大且灵活的开源安全框架。Shiro 专注于简化身份验证、授权、会话管理和加密等复杂的安全操作,使开发者能够更轻松地保护应用程序。其核心目标是提供易于使用和理解的API,同时确保高度的安全性和灵活性。 ... [详细]
  • 华为USG基于源地址的多出口策略路由配置
    网络拓扑如下:组网情况:企业用户主要有技术部(VLAN10)和行政部(VLAN20),通过汇聚交换机连接到USG。企业分别通过两个不同运营商(ISP1和ISP2)连接到 ... [详细]
  • 本文将详细探讨Linux pinctrl子系统的各个关键数据结构,帮助读者深入了解其内部机制。通过分析这些数据结构及其相互关系,我们将进一步理解pinctrl子系统的工作原理和设计思路。 ... [详细]
  • PHP 过滤器详解
    本文深入探讨了 PHP 中的过滤器机制,包括常见的 $_SERVER 变量、filter_has_var() 函数、filter_id() 函数、filter_input() 函数及其数组形式、filter_list() 函数以及 filter_var() 和其数组形式。同时,详细介绍了各种过滤器的用途和用法。 ... [详细]
  • 使用lambda表达式排序Collections.sort(temp,(Stringa,Stringb)-{returnb.compareTo(a);});Collections ... [详细]
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社区 版权所有