Go 实现 MySQL 数据库事务
一、MySQL事务
MySQL事务是指一组数据库操作,它们被视为一个逻辑单元,并且要么全部成功执行,要么全部回滚(撤销)。事务是数据库管理系统提供的一种机制,用于确保数据的一致性和完整性。
事务具有以下特性(通常由ACID原则定义):
- 原子性(Atomicity):事务中的所有操作要么全部成功执行,要么全部回滚,不存在部分执行的情况。如果事务中的任何一个操作失败,则所有操作都会被回滚到事务开始之前的状态,保持数据的一致性。
- 一致性(Consistency):事务的执行使数据库从一个一致的状态转换到另一个一致的状态。这意味着在事务开始和结束时,数据必须满足预定义的完整性约束。
- 隔离性(Isolation):事务的执行是相互隔离的,即一个事务的操作在提交之前对其他事务是不可见的。并发事务之间的相互影响被隔离,以避免数据损坏和不一致的结果。
- 持久性(Durability):一旦事务提交成功,其对数据库的更改将永久保存,即使在系统故障或重启之后也能保持数据的持久性。
在MySQL中,使用以下语句来开始一个事务:在事务中,可以执行一系列的数据库操作,如插入、更新和删除等。最后,使用以下语句来提交事务或回滚事务:
提交事务:回滚事务:通过使用事务,可以确保数据库操作的一致性和完整性,尤其在处理涉及多个相关操作的复杂业务逻辑时非常有用。
二、MySQL 事务 示例
以下是一个示例,演示如何在MySQL中使用事务:- START TRANSACTION;
- -- 在事务中执行一系列数据库操作
- INSERT INTO users (name, age) VALUES ('Alice', 25);
- UPDATE accounts SET balance = balance - 100 WHERE user_id = 1;
- DELETE FROM logs WHERE user_id = 1;
- -- 如果一切正常,提交事务
- COMMIT;
复制代码 在上述示例中,我们开始了一个事务,并在事务中执行了一系列数据库操作。
首先,我们向users表插入了一条新记录,
然后更新了accounts表中用户ID为1的账户余额,
最后删除了logs表中与用户ID为1相关的日志条目。
如果在事务执行的过程中出现任何错误或异常情况,可以使用ROLLBACK语句回滚事务,使所有操作都被撤销,数据库恢复到事务开始之前的状态:- START TRANSACTION;
- -- 在事务中执行一系列数据库操作
- INSERT INTO users (name, age) VALUES ('Bob', 30);
- UPDATE accounts SET balance = balance - 200 WHERE user_id = 2;
- -- 发生错误或异常,回滚事务
- ROLLBACK;
复制代码 在上述示例中,如果在更新accounts表的操作中发生错误,整个事务将被回滚,插入的用户记录和更新的账户余额将被撤销。
事务的关键在于将多个相关的数据库操作组织在一起,并以原子性和一致性的方式进行提交或回滚。这确保了数据的完整性和一致性,同时也提供了灵活性和错误恢复机制。
三、MySQL 事务引擎
MySQL提供了多个事务引擎,每个引擎都具有不同的特性和适用场景。以下是MySQL中常见的事务引擎:
- InnoDB:InnoDB是MySQL默认的事务引擎。它支持事务、行级锁定、外键约束和崩溃恢复等功能。InnoDB适用于需要强调数据完整性和并发性能的应用程序。
- MyISAM:MyISAM是MySQL的另一个常见的事务引擎。它不支持事务和行级锁定,但具有较高的插入和查询性能。MyISAM适用于读密集型应用程序,例如日志记录和全文搜索。
- NDB Cluster:NDB Cluster是MySQL的集群事务引擎,适用于需要高可用性和可扩展性的分布式应用程序。它具有自动分片、数据冗余和故障恢复等功能。
- Memory:Memory(也称为Heap)引擎将表数据存储在内存中,提供非常高的插入和查询性能。但由于数据存储在内存中,因此在数据库重新启动时数据会丢失。Memory引擎适用于临时数据或缓存数据的存储。
除了以上列出的常见事务引擎之外,MySQL还支持其他一些事务引擎,例如Archive、Blackhole等。每个引擎都有其独特的特性和适用场景,选择合适的事务引擎需要根据应用程序的需求和性能要求进行评估。
在创建表时,可以指定所需的事务引擎。例如,使用以下语句创建一个使用InnoDB引擎的表:- CREATE TABLE mytable (
- id INT PRIMARY KEY,
- name VARCHAR(50)
- ) ENGINE=InnoDB;
复制代码 需要注意的是,不同的事务引擎可能会有不同的配置和限制,因此在选择和使用特定的事务引擎时,建议参考MySQL文档以了解详细信息和最佳实践。
四、事务实例
开启事务 Begin 源码:
- // BeginTx starts a transaction.
- //
- // The provided context is used until the transaction is committed or rolled back.
- // If the context is canceled, the sql package will roll back
- // the transaction. Tx.Commit will return an error if the context provided to
- // BeginTx is canceled.
- //
- // The provided TxOptions is optional and may be nil if defaults should be used.
- // If a non-default isolation level is used that the driver doesn't support,
- // an error will be returned.
- func (db *DB) BeginTx(ctx context.Context, opts *TxOptions) (*Tx, error) {
- var tx *Tx
- var err error
- err = db.retry(func(strategy connReuseStrategy) error {
- tx, err = db.begin(ctx, opts, strategy)
- return err
- })
- return tx, err
- }
- // Begin starts a transaction. The default isolation level is dependent on
- // the driver.
- //
- // Begin uses context.Background internally; to specify the context, use
- // BeginTx.
- func (db *DB) Begin() (*Tx, error) {
- return db.BeginTx(context.Background(), nil)
- }
复制代码 中止事务 Rollback 源码:
- // rollback aborts the transaction and optionally forces the pool to discard
- // the connection.
- func (tx *Tx) rollback(discardConn bool) error {
- if !tx.done.CompareAndSwap(false, true) {
- return ErrTxDone
- }
- if rollbackHook != nil {
- rollbackHook()
- }
- // Cancel the Tx to release any active R-closemu locks.
- // This is safe to do because tx.done has already transitioned
- // from 0 to 1. Hold the W-closemu lock prior to rollback
- // to ensure no other connection has an active query.
- tx.cancel()
- tx.closemu.Lock()
- tx.closemu.Unlock()
- var err error
- withLock(tx.dc, func() {
- err = tx.txi.Rollback()
- })
- if !errors.Is(err, driver.ErrBadConn) {
- tx.closePrepared()
- }
- if discardConn {
- err = driver.ErrBadConn
- }
- tx.close(err)
- return err
- }
- // Rollback aborts the transaction.
- func (tx *Tx) Rollback() error {
- return tx.rollback(false)
- }
复制代码 提交事务 Commit 源码:
[code]// Commit commits the transaction.func (tx *Tx) Commit() error { // Check context first to avoid transaction leak. // If put it behind tx.done CompareAndSwap statement, we can't ensure // the consistency between tx.done and the real COMMIT operation. select { default: case |