当前位置:首页 > 问答 > 正文

数据库老是锁住咋整,频繁锁定问题到底怎么破?

这个问题说白了,就是数据库在处理很多人同时干活儿的时候,为了防止数据被改乱,会像给一个房间上把锁一样,只让一个人进去操作,但问题是,这把锁有时候锁的时间太长,或者锁的范围太大,导致其他想干活儿的人都在门口排队等着,系统就卡住了,反应变得巨慢,甚至直接报错,要解决这个问题,咱们得从几个最常出毛病的地方下手,别慌,一步步来。

第一,先看看是不是那些写得不好的SQL语句在捣鬼。 这是最常见的原因,没有之一,很多程序员写完代码,功能测试没问题就完事儿了,根本没在意数据库底层是怎么执行他写的那些命令的。(根据《高性能MySQL》等资料中的普遍观点)一条SQL语句如果没利用好索引,或者一次性要处理的数据量太大,它就可能不是只锁住它需要的那一小行数据,而是会锁住一大片,甚至锁住整个表,你想啊,它自己在那儿慢吞吞地全表扫描,就像一个人在房间里整理所有柜子,还把门反锁了,外面谁也别想进,破局的关键第一步就是优化这些慢查询SQL,怎么优化?你得先把这些“罪魁祸首”找出来,数据库一般都有慢查询日志功能,把它打开,让它记录下所有执行时间超过一定阈值(比如1秒)的语句,然后你就盯着这些慢语句,重点看它有没有用上索引,你经常要根据用户ID查信息,那用户ID这个字段上就必须建索引,还有就是,尽量避免在SQL的where条件里对字段进行函数计算,比如where DATE(create_time) = '2023-01-01',这样会导致索引失效,改成where create_time >= '2023-01-01' and create_time < '2023-01-02'效果就好多了。

数据库老是锁住咋整,频繁锁定问题到底怎么破?

第二,检查一下你的事务是不是太“长”了。 事务就是数据库里的一组操作,要么全部成功,要么全部失败,但有个大忌就是长事务。(参考数据库基础理论中的事务隔离级别与锁的关系)你在一个事务里,先查询了一些数据,然后中间可能调用了其他耗时的服务,或者干脆等着用户在前端页面慢慢输入信息,最后才提交事务,在这个过程中,这个事务可能一直占着某些锁不释放,其他人想修改这些被锁住的数据,就只能干等着,这就像你去银行柜台办业务,柜员把你的存折要过去开始处理,然后中途接了个很长的私人电话,你和其他后面排队的人全都得等着,急死人,解决办法就是尽量缩短事务的执行时间,要把事务设计得小巧玲珑,快进快出,特别是,不要把一些与数据库操作无关的、耗时的业务逻辑(比如调用外部API、复杂的计算、等待用户输入)放在数据库事务内部,确保在事务里只做必要的数据库操作,一做完立马提交,尽快释放锁资源。

第三,考虑一下是不是隔离级别设得太高了。 数据库为了平衡数据一致性和并发性能,设置了几种不同的“隔离级别”,级别越高,数据越安全,但并发性能越差,锁的问题也越容易出现。(基于ANSI SQL标准对隔离级别的定义)默认的“可重复读”级别,为了防止其他事务修改你正在读的数据,可能会加一些共享锁,而最高的“串行化”级别,为了保证绝对不出错,限制就更严格,对于大多数常见的互联网应用来说,其实并不需要这么高的隔离级别,你可以尝试将数据库的隔离级别适当调低,比如降到“读已提交”,在这个级别下,读数据时一般不会加锁(或者很快释放),写操作才加锁,这样能大大减少锁冲突,提升并发能力,调低隔离级别之前,你得评估一下你的业务是否能接受这种改变可能带来的极少数特殊情况(比如不可重复读),但说实话,很多业务场景下,这点风险是完全可以接受的,换来的性能提升却是实实在在的。

数据库老是锁住咋整,频繁锁定问题到底怎么破?

第四,看看代码里有没有“死锁”的隐患。 死锁是更讨厌的情况,就是两个事务互相等着对方释放锁,结果谁都进行不下去,数据库最后只能强制回滚其中一个。(操作系统和数据库原理中经典的死锁四必要条件)事务A先锁住了记录1,然后想去锁记录2;同时事务B先锁住了记录2,然后想去锁记录1,俩人就这么僵住了,要避免死锁,一个很实用的原则就是让所有业务操作都按照一个固定的顺序去访问数据,比如说,但凡需要同时更新用户表和订单表,大家约定俗成,都先更新用户表,再更新订单表,这样就能从根本上避免这种循环等待的局面,尽量在一次请求中减少涉及的表和记录数,也是降低死锁概率的好办法。

如果上面这些软件层面的优化都做了,问题还是特别严重,那就得想想硬件和架构层面的升级了。(常见的数据库扩展方案)

  • 读写分离:搞一个主数据库专门负责写数据,然后挂好几个从数据库专门负责读数据,这样就把读的压力分散出去了,写库的锁压力自然就小了,大部分查询操作都去从库做,速度会快很多。
  • 分库分表:如果单个数据库实在撑不住了,可以把一个大库拆分成多个小库,或者把一个大表按某种规则(比如用户ID尾号、时间)拆分成多个小表,这样数据分散了,锁的竞争也就分散了。
  • 升级硬件:有时候可能就是数据库服务器本身的CPU、内存或者磁盘IO跟不上趟了,导致处理速度慢,锁自然就释放得慢,适当提升硬件配置,尤其是使用更快的SSD硬盘,也能立竿见影地缓解问题。

解决数据库频繁锁定的问题,没有一招鲜的妙药,它是一个系统性的排查和优化过程,你得像个侦探一样,从SQL语句、事务设计、数据库配置、业务代码等多个角度去分析,找到那个真正的瓶颈点,然后对症下药。