避免锁表:为Update语句中的Where条件添加索引字段

说实话,我以前在外包项目中也遇到过这样的情况,相当令人不安。
当时有一个系统,用户量很少,但是时不时就崩溃。
后来发现UPDATE语句没有索引。
你说的场景完全正确。

我会在这里给你我的经验。
当时我们使用的是InnoDB引擎,表结构由自增id和状态字段组成。
每天清晨都有一个计划任务来更改数据集的状态。
这个作业写得不好。
它直接更新所有status=0的行,结果整个表被锁定。
后台运维完全混乱,系统半夜静止不动。

查资料后发现,为了实现行级锁定,InnoDB需要知道哪些行需要改变。
如果您的 WHERE 条件没有索引,则需要全表扫描。
扫描、扫描并锁定要更改的行。
当这数千行都被锁定时,其他想要插入、更新甚至读取的查询将不得不在队列中等待。
您提到更新 markId=1 8 如果markId没有索引的话,整个表都会被阻塞。

有趣的是,我们更改索引后,计划任务运行得好多了。
但后来我又遇到了麻烦。
之后我们添加了一个新的字段优先级并进行了复杂的逻辑判断。
事实证明,即使状态字段已建立索引,整个 WHERE 子句也使用优先级。
该字段不被索引,并且锁被再次释放。
我当时就很困惑,索引不就解决问题了吗?后来查了MySQL文档,发现索引失败的情况相当复杂。
例如,函数运算、类型转换或计算字段可能会使索引无效。

我基本上已经使用了你列出的所有命令。
特别是,performance_schema 是诊断死锁问题的强大工具。
上次我遇到了一个问题,卡住了十分钟。
最后用data_lock_waits查看,发现有两个事务在等待对方的锁。
一个更新order_id没有索引,另一个插入新订单,导致死锁。
将order_id添加到索引中或者调整事务隔离级别即可解决问题。

说白了,加了索引并不意味着一切就万事大吉了。
这取决于实际使用的字段、计算方法甚至数据分布。
你说WHERE条件字段必须有索引。
这是绝对正确的。
但更具体地说,这取决于执行计划。
我有一个客户,他的表有 3 000 万行。
它增加了索引,但是执行计划仍然使用全表扫描。
后来发现数据非常稀疏,索引页的使用率很低。
最后,使用覆盖索引解决了这个问题。

我个人没有处理过分布式场景下的死锁问题,不过好像比较复杂。
例如,由于数据库被分区为表,因此可能需要单独讨论跨事务锁或分布式事务锁协调。
但对于单一形式的发动机锁,你提到的想法是绝对合理的。
尤其是像markId这样经常用于条件查询的字段,更应该添加索引。
如果确实遇到索引失败,除了检查执行计划外,还应该检查字段类型是否转换,是否使用函数计算情况。

我记得数据是关于X的,但我建议你检查performance_schema版本兼容性。
不同版本中某些命令可能会有所不同。

MySQL 给数据表增加一列,一定会锁表吗?

我曾经花了一个周末下午向公司的一个大型数据库表添加列。
这些表有数百万条记录,通常业务量很大。
我使用的是 MySQL 5 .6 版本。
这次我认为添加一列会锁定表并使速度变慢。
但是,当我运行 ALTER TABLE 命令时,我确认该表没有被锁定,并且不影响工作。
我当时有点惊讶,因为我之前的经验是这些操作通常会导致短表锁。

等一下,我突然想到MySQL 5 .6 版本开始支持在线DDL操作。
当我这次添加列时,表似乎没有被锁定,这可能就是原因。
但我没有详细研究过这个功能,只是依稀记得。

现在我了解到,不同版本的 MySQL 在执行类似任务时具有不同的锁表行为。
这也提醒我们,在处理数据库操作时,不仅要考虑操作本身,还要注意数据库的版本和性质。
毕竟,微小的版本差异就可能导致完全不同的操作体验。

面试官:MySQL 给数据表增加一列,一定会锁表吗?

说得直白一点:InnoDB 在添加列时不应该锁定表,这取决于版本和实现。
MySQL5 .6 :
新的非空列:更快的重复数据删除操作,无表锁定。

新的可删除列:更快的数据库操作,没有表锁,也可能没有行锁。
MySQL8 .0:
原子DDL:其他会话继续读写
立即更新元数据:减少锁定时间。

非空字段:轻量级操作,不复制整个表数据。
例外情况:
大数据表:元数据操作可能会被锁定一小段时间。

复杂操作:多个字段更新可能会导致重新锁定。
较低版本:5 .5 及以下肯定会锁表。
温馨提示:
使用最高版本:8 .0+进行优化。

非凡的工作:减少影响。
监控性能:监控负载和响应。

测试环境验证:初步评估。

当前版本足够吗?