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

数据库锁怎么弄啊,创建的时候需要注意啥问题和步骤?

关于数据库锁怎么弄,首先需要理解锁不是像创建一张表或者一个索引那样有一个明确的“创建”命令,锁是数据库管理系统在背后自动管理的一种机制,用来保证数据的一致性。“弄”锁的核心在于我们编写的SQL语句和数据库的配置,这些操作会触发数据库自动加锁,换句话说,我们是通过控制自己的行为来影响数据库的加锁策略。

(根据关系数据库通用原理和常见数据库如MySQL、Oracle的文档概述)

锁是如何被触发的?

当你对数据库进行操作时,锁就自动产生了,最常见的两种情况是:

  1. 当你修改数据时(写操作):比如你执行 UPDATE(更新)、DELETE(删除)、INSERT(插入)语句,为了保证在你修改的期间,没有其他人能同时修改或读取到未确定的中间状态数据,数据库会自动对你正在操作的那一行(或一批数据)加上一种叫做“排他锁”(X锁)的锁,这个锁就像你进卫生间后从里面反锁一样,在你出来(事务提交或回滚)之前,别人既进不来(不能修改),也看不到里面的情况(在某些隔离级别下不能读取)。
  2. 当你查询数据时(读操作):这稍微复杂一些,取决于数据库的“事务隔离级别”设置,如果你只是想快速地、大概地读一下数据,不要求绝对精确,数据库可能什么都不加锁,直接从某个历史快照里读数据,但如果你在执行一个很严谨的查询,并且不希望在你读的过程中,数据被别人修改,你可以在查询语句后加上 FOR UPDATE 这样的后缀(具体语法因数据库而异),这时,数据库就会给你查询到的那些行加上一种“共享锁”(S锁),共享锁允许多个人同时读,就像很多人可以同时看同一本书一样,但任何一个人都不能在看书的时候在书上乱写乱画(不能加排他锁进行修改)。

“弄”锁的第一步,就是写好你的SQL语句,并理解你当前数据库会话的事务隔离级别设置。

创建和使用锁时需要特别注意的问题和步骤

虽然锁是自动的,但如果我们使用不当,就会引发严重问题,最主要的问题就是“死锁”。

数据库锁怎么弄啊,创建的时候需要注意啥问题和步骤?

(根据数据库并发控制理论及故障处理手册)

什么是死锁? 可以想象一个场景:两个人A和B在一条窄路上相遇,A需要B让开才能过去,B也需要A让开才能过去,两个人都在等对方先动,结果谁都动不了,在数据库里,就是两个或多个事务互相等待对方释放锁,导致所有事务都无法继续执行。

步骤和注意事项:

  1. 事务要尽可能短小精悍:这是最重要的原则,一个事务里包含的SQL操作越少,它持有锁的时间就越短,就像你去银行柜台办业务,如果你只是存个钱,一分钟办完,后面的人等一小会儿;但如果你又要存钱、又要办贷款、又要挂失,搞半个小时,后面排队的人就急死了,整个系统的并发性就差了,不要在事务里做无关的查询或复杂的计算,更不要在事务中间让人工交互(比如等用户输入),要尽快提交或回滚事务。

    数据库锁怎么弄啊,创建的时候需要注意啥问题和步骤?

  2. 访问资源的顺序要一致:这是避免死锁的关键技巧,假设你的应用有两个业务逻辑,都需要更新表A和表B,如果逻辑一总是先更新A再更新B,而逻辑二总是先更新B再更新A,那么在并发时就有可能发生死锁,事务1锁住了A,试图锁B;同时事务2锁住了B,试图锁A,死锁就发生了,解决办法是,在设计和编码时,规定一个固定的顺序,比如所有需要同时更新A和B的业务,都必须先更新A,再更新B,这样,事务之间就不会出现循环等待。

  3. 基于主键或唯一索引进行更新:当你使用 UPDATE ... WHERE ...DELETE ... WHERE ... 时,WHERE条件尽量使用主键或唯一索引,因为这样数据库可以精准地锁定一行数据,如果你用的是非索引字段,UPDATE users SET status=1 WHERE name='张三',而name字段上没有索引,数据库为了安全起见,可能无法精确知道要锁哪几行,它可能会锁住整个表,或者锁住一大片数据范围(锁升级),这会对并发性能造成灾难性影响。

  4. 合理设置事务隔离级别:事务隔离级别越高(如“可串行化”),数据一致性保障越强,但数据库加的锁就越多、范围可能越大,并发性能就越低,而级别越低(如“读已提交”),并发性能越好,但可能会遇到“不可重复读”、“幻读”等问题,你需要根据业务的真实需求来权衡,比如一个普通的查询网站,可能不需要最高的隔离级别;而一个金融交易系统,则可能必须采用高级别隔离,不要一味追求最高级别。

  5. 准备好处理死锁异常:即使我们注意了以上几点,在复杂的系统中死锁仍可能发生,成熟的数据库(如MySQL、Oracle)都有死锁检测机制,一旦发现死锁,会立即强制回滚其中一个事务,让其他事务得以继续,并向被回滚的事务返回一个错误。在你的应用程序代码中,必须捕获并处理这类死锁异常,最常见的处理方式就是“重试”:当捕获到死锁错误后,等待一个非常短的时间(比如几十到几百毫秒),然后重新执行整个失败的事务,通常重试几次后就能成功。

  6. 避免在业务高峰期的长事务和大批量操作:比如在白天用户活跃的时候,尽量避免执行需要更新几十万行数据的报表生成任务或数据归档任务,这种操作会长时间持有大量锁,阻塞正常业务操作,这类任务最好安排在系统负载低的夜间进行。

数据库锁的核心在于“理解”而非“创建”,我们需要通过编写高效、短小的事务,遵循一致的资源访问顺序,并使用合适的查询条件,来引导数据库以最合理的方式加锁,从而在保证数据正确性的前提下,获得最好的并发性能,要有完善的异常处理机制来应对不可避免的死锁情况。