没有索引时,FOR UPDATE
会锁住整个表
现在,你正在一本一本地翻看所有书,寻找“维修中”的书,并且你对管理员说:“在我清点和修改完之前,别人不能动这些书,也不能往这个范围里加新书!”
-
问题1:如何锁住你找到的“维修中”的书?
你每找到一本“维修中”的书,就给它贴上一个“正在处理,请勿触碰”的标签(行级排他锁)。 -
问题2:如何防止别人“往这个范围里加新书”?
这是最关键的。因为你没有“状态”的目录卡片(没有索引),你不知道“维修中”的书在图书馆的哪个具体位置。它们可能散落在图书馆的任何角落。- 你无法像有索引那样,只锁住“计算机类”书架的某个空位(间隙锁)。
- 你正在全图书馆地毯式搜索。
- 如果允许其他管理员在你搜索的时候,随便往任何一个空书架上放一本新的“维修中”的书,那么你就会遇到“幻读”——你清点完后,发现多出了几本你没见过的“维修中”的书。
唯一的办法就是:
为了确保在你搜索和处理期间,没有任何新的“维修中”的书被偷偷放进来,图书馆管理员只能宣布:
“暂停营业!所有人都不能动任何书,也不能放新书进来,直到这位管理员清点完毕!”
这就是表级排他锁。它锁住了整个图书馆,确保了在你操作期间,没有任何新的“维修中”的书能够被插入,从而彻底避免了幻读。
总结一下:
当 WHERE
条件中的字段没有索引时,数据库无法精确地定位和锁定数据范围。为了满足 FOR UPDATE
语句防止幻读的要求,它不得不采取最保守、最安全的策略——锁定整个表。这就像为了防止一只老鼠跑进屋子,你把整个房子的大门都锁死了。虽然有效,但代价是牺牲了其他人的自由(并发性)。
所以,为了提高数据库的效率和并发性,对于那些经常用于 WHERE
条件,特别是用于 FOR UPDATE
的字段,强烈建议创建索引。