事务 (transaction)

本文深入讲解了数据库事务的概念,包括其基本定义、四大特性(原子性、一致性、隔离性和持久性),以及如何通过提交和回滚操作来管理事务。此外,还详细探讨了四种不同的事务隔离级别及其应用场景。

        一个事务就是一个完整的业务逻辑。举例:A向B转账10000,即A减去10000,B加上10000。这个操作是一个最小的工作单元,必须一起成功或失败。

        只有DML语句才有事务的说法,因为只有insert、update、delete才是对数据进行增删改。而一旦对数据进行操作,必须考虑安全问题。事务本质是批量的DML语句,同时成功或者失败。

        事务包括四个特性:(1)原子性:事务是最小的工作单元;(2)一致性:同一事务中所有操作必须同时成功或者失败;(3)隔离性:A事务和B事务之间有一定的隔离,如两个事务同时操作一张表;(4)持久性:事务最终结束的保障。

一、事务的实现

        在事务执行过程中,每一条事务都会记录到InnoDB提供的日志中,既可以提交事务,也可以回滚事务。

        提交事务:清空事务性活动的日志文件,将数据永久储存到数据库表中。标志着事务的结束,并且是成功结束。

        回滚事务:也要清空事务性活动的日志文件,将之前的操作全部撤销,标志着事务的失败结束。

        提交和回滚事务:commit、rollback;

        rollback只能回滚到上一次的提交点,由于mysql有自动提交机制,运行一行就会提交,所以需要手动关闭它的自动提交:

        start transaction; 

二、事务隔离级别      

        隔离级别有四个:

                读未提交:read uncommitted(最低的隔离级别)

// 事务A可读到事务B未提交的数据。存在脏读现象。这种隔离级别是理论的。

                读已提交:read committed

//事务A只能读取到事务B已提交的数据。解决了脏读现象。不可重复读取数据(可能A事务刚开始读到3条,下一次就读到4条)。该级别是oracle默认的隔离级别,读到的数据很真实。

                可重复读:repeatable read

//事务A开启之后,读取到B的数据都是一样的,即使B事务提交新的。解决了不可重复读的问题,可能会出现幻影问题,数据不够真实。

                序列化读:serializable(最高的隔离级别)

// 效率最低,各个事务需要排队执行,不能并发,每次读到的数据都是最真实的。

演示:

        //查看隔离级别:

                select @@transaction_isolation;

        //设置隔离级别,需要设置后关闭并重新进入数据库

                set global transaction isolation level 隔离级别;

演示结果:在read uncommitted的隔离级别下,事务B只要执行了对表的操作,事务A都可以读取到表的改动。而在read committed的隔离级别下,只有事务B执行了commit,之前的所有操作上传到数据库中的表,事务A才能读取到这些操作。

        在repeatable read的隔离级别下,事务A中读到的结果总是跟它第一次读到的结果一样,不管事务B怎么进行提交。除非事务A进行提交,A才会新读到数据。

        在serializable级别下,事务A如果对表进行了操作,事务B对同一张表进行操作就会卡住不动,除非事务A提交或回滚结束事务。

        

        

        

### 3.1 驱动兼容性问题 Qt 对 MySQL 的事务支持依赖于底层驱动的实现。如果使用的 MySQL 驱动(`qsqlmysql.dll`)版本与 MySQL 客户端库不兼容,可能导致事务功能不可用。某些版本的 MySQL Connector/C(如 8.0.x)在 Qt 的默认驱动中并未完全实现事务控制功能,导致调用 `QSqlDatabase::transaction()` 时返回 `false`。 解决方法是重新编译 Qt 的 MySQL 插件,确保使用的 `libmysql.dll` 与驱动编译时的版本一致。例如,使用 `mysql-connector-c-6.1.11-winx64.zip` 替代更高版本的 Connector/C,可提高兼容性[^2]。 ### 3.2 驱动加载失败导致事务不可用 如果 Qt 无法正确加载 `QMYSQL` 驱动,事务操作将无法执行。即使代码中指定了 `"QMYSQL"` 驱动,如果驱动未正确部署或依赖库缺失(如 `libmysql.dll` 未放置在可执行文件目录或系统路径中),`QSqlDatabase::addDatabase("QMYSQL")` 调用将失败,但不会立即抛出异常。此时调用 `transaction()` 方法将无效。 确保部署时将 `libmysql.dll` 和 Qt 的数据库插件路径(如 `plugins/sqldrivers`)一同打包,并在程序启动时检查支持的数据库驱动: ```cpp qDebug() << QSqlDatabase::drivers(); ``` 若输出中未包含 `"QMYSQL"`,说明驱动未正确加载,事务功能将不可用[^3]。 ### 3.3 存储引擎不支持事务 MySQL 的事务支持依赖于存储引擎。如果目标表使用的是 `MyISAM` 等不支持事务的引擎,即使 Qt 和 MySQL 驱动配置正确,事务操作(如 `BEGIN`, `COMMIT`, `ROLLBACK`)也不会生效。此时调用 `transaction()` 方法可能返回 `true`,但在提交或回滚时不会产生预期效果。 应确保操作的表使用支持事务的引擎(如 `InnoDB`),可通过以下 SQL 语句验证: ```sql SELECT ENGINE FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'your_database' AND TABLE_NAME = 'your_table'; ``` 若返回的 `ENGINE` 不是 `InnoDB`,需执行以下语句进行转换: ```sql ALTER TABLE your_table ENGINE = InnoDB; ``` 此操作可确保事务操作在数据库层面被正确支持[^1]。 ### 3.4 事务隔离级别与并发控制 MySQL 的事务行为受隔离级别影响。默认情况下,MySQL 使用 `REPEATABLE-READ` 隔离级别,可能影响事务的可见性和提交行为。在高并发环境下,事务可能因锁等待或死锁而失败。 可通过以下语句查看当前隔离级别: ```sql SELECT @@tx_isolation; ``` 如需调整,可在 MySQL 配置文件中设置: ```ini [mysqld] transaction-isolation = READ-COMMITTED ``` 重启 MySQL 服务后生效。此操作可优化事务并发行为,减少因隔离级别导致的事务失败[^1]。 ### 3.5 多连接与事务绑定问题 Qt 中事务绑定于单个数据库连接。若在事务开启后切换了数据库连接(如使用多个 `QSqlDatabase` 实例),事务将失效。应在事务执行期间确保使用相同的数据库连接实例,并避免在事务过程中执行 `use database` 或切换连接的操作。 例如,以下代码确保事务在单一连接上执行: ```cpp QSqlDatabase db = QSqlDatabase::database(); // 获取默认连接 if (!db.transaction()) { qDebug() << "事务启动失败:" << db.lastError().text(); } ``` 确保事务与连接绑定,可避免因连接切换导致的事务失败[^1]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值