> 文章列表 > 数据库事务

数据库事务

数据库事务

什么是事务

数据库中,事务(Transaction)是指一组数据库操作,这些操作要么全部成功执行,要么全部失败回滚,是保证数据库操作一致性的基本单位。事务具有原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)四个特性。

事务的特性

原子性(Atomicity)

        原子性是指事务中的所有操作要么全部成功完成,要么全部回滚到事务开始前的状态。即事务中的所有操作要么全部执行,要么全部不执行,不会出现执行了一半的情况。

一致性(Consistency)

        一致性是指事务执行前后,数据库的完整性约束没有被破坏。在事务开始和结束时,数据库的完整性约束都得到保持,比如外键约束、唯一性约束等。

隔离性(Isolation)

        隔离性是指多个事务并发执行时,彼此之间是相互隔离的,一个事务的执行不应该受到其他事务的干扰。即使多个事务同时访问同一个数据,也不能相互影响,每个事务感觉自己独立地操作数据。为了保证隔离性,数据库系统采用了多种隔离级别,如下文所述。

持久性(Durability)

        持久性是指事务一旦提交成功,对数据库的修改就是永久性的,不会因为数据库崩溃或其他原因导致数据丢失。即使系统发生故障,也能够保证数据不会丢失。

隔离级别

        隔离级别是指多个事务并发执行时,一个事务对数据的修改对其他事务的可见程度。常见的隔离级别有四种,分别是读未提交(Read uncommitted)、读已提交(Read committed)、可重复读(Repeatable read)和串行化(Serializable)。

读未提交(Read uncommitted)

        读未提交是最低的隔离级别,一个事务可以读取其他事务未提交的数据,可能会导致脏读(Dirty read)问题,即读到了其他事务已经修改但未提交的数据。

读已提交(Read committed)

        读已提交是指一个事务只能读取已经提交的其他事务的数据,可以避免脏读问题。但是由于其他事务正在执行,所以可能出现不可重复读(Non-repeatable read)问题,即同一个事务内读取同一数据两次,结果不一致。

可重复读(Repeatable read)

        可重复读是指同一个事务内多次读取同一数据时,结果是一致的,即使其他事务修改了该数据,也不会对该事务造成影响。可重复读可以避免不可重复读问题,但是可能会出现幻读(Phantom read)问题,即同一个事务内两次查询,结果不一致,因为其他事务在此期间插入了符合条件的数据。

串行化(Serializable)

        串行化是最高的隔离级别,强制所有事务串行执行,避免了脏读、不可重复读和幻读等问题。但是由于强制串行执行,导致并发性能非常差,一般不推荐使用。

为什么要使用事务

        使用事务的主要目的是保证数据库中的数据一致性和完整性。在现实生活中,许多业务场景都需要保证多个操作作为一个整体进行,而不是单独执行,这时候就需要使用事务来保证这些操作的原子性,也就是要么全部执行成功,要么全部失败,保证数据的一致性。

举个例子,假设我们要从银行账户A向账户B转账100元。这个过程需要进行两个操作:将账户A的余额减去100元,将账户B的余额增加100元。如果没有事务保证,当这两个操作分别执行时,如果在这两个操作之间发生了系统崩溃、网络中断等异常情况,就有可能导致A账户扣款成功但B账户未收到款项,从而导致数据的不一致性。使用事务可以保证这两个操作作为一个整体进行,如果在操作过程中出现异常,可以回滚事务,保证数据的完整性。

此外,使用事务还可以避免多个客户端并发修改同一个数据造成的数据冲突问题,比如脏读、不可重复读、幻读等。通过设置不同的隔离级别,可以平衡并发性能和数据一致性之间的关系。

因此,使用事务可以有效保证数据库中数据的一致性和完整性,同时避免多个客户端并发修改同一个数据造成的数据冲突问题。

案例演示:

        展示如何使用事务来保证一组 SQL 操作的原子性。

        假设我们有两个表,一个是用户表 user,一个是订单表 order,其中订单表记录了每个用户的订单信息。现在我们要实现一个功能,当用户下单时,需要先将用户的余额扣减相应的金额,然后再在订单表中插入一条订单记录。

首先,我们创建表:

CREATE TABLE user (id INT PRIMARY KEY AUTO_INCREMENT,name VARCHAR(50) NOT NULL,balance DECIMAL(10, 2) NOT NULL DEFAULT 0.00
);CREATE TABLE order (id INT PRIMARY KEY AUTO_INCREMENT,user_id INT NOT NULL,amount DECIMAL(10, 2) NOT NULL,created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);

接下来,我们可以使用以下的 SQL 语句来实现这个功能:

START TRANSACTION; -- 开始事务UPDATE user SET balance = balance - 100.00 WHERE id = 1; -- 扣减用户余额INSERT INTO order (user_id, amount) VALUES (1, 100.00); -- 插入订单记录COMMIT; -- 提交事务

        在上面的代码中,我们首先使用 START TRANSACTION 开始一个事务,然后执行扣减用户余额的操作和插入订单记录的操作,最后使用 COMMIT 提交事务。如果在执行这些操作的过程中发生了任何错误,我们可以使用 ROLLBACK 回滚事务,保证数据的完整性和一致性。

        需要注意的是,如果使用 InnoDB 引擎,MySQL 默认的隔离级别是 REPEATABLE READ。这种隔离级别可以避免脏读、不可重复读、幻读等问题,但是也可能导致性能问题。如果需要平衡并发性能和数据一致性,可以考虑使用更低的隔离级别,比如 READ COMMITTEDREAD UNCOMMITTED