MySql操作指南5--事务与并发控制

news/2025/1/19 10:26:58/

数据库事务是保障数据一致性和可靠性的重要手段,并发控制则在多用户环境下确保数据的正确性。风云今天详细探讨数据库事务的管理、并发访问的最佳实践,乐观锁与悲观锁的应用,以及Golang 中的事务实现、并发访问的最佳实践,通过合理设计事务和锁机制,可以在高并发场景下有效保障数据的一致性和系统性能。

1、数据库事务管理

事务是一个操作序列,具有以下四个特性(ACID):

原子性(Atomicity):事务中的操作要么全部完成,要么全部回滚。

一致性(Consistency):事务前后,数据库保持一致性状态。

隔离性(Isolation):事务之间相互隔离,避免相互干扰。

持久性(Durability):事务完成后,其对数据库的更改永久生效。

2、在 Golang 中实现事务

 示例:事务的基本操作

package mainimport ("database/sql""fmt""log"_ "github.com/go-sql-driver/mysql"
)func main() {// 数据库连接dsn := "user:password@tcp(127.0.0.1:3306)/testdb" // dsn格式db, err := sql.Open("mysql", dsn) // 打开数据库连接if err != nil { // 如果连接失败,则输出错误信息并退出程序log.Fatal(err)}defer db.Close() // 关闭数据库连接// 开启事务tx, err := db.Begin() // 开启事务if err != nil {log.Fatal(err)}// 执行操作_, err = tx.Exec("INSERT INTO accounts (id, balance) VALUES (?, ?)", 1, 100) // 执行操作if err != nil { // 如果执行失败,则回滚事务并输出错误信息并退出程序tx.Rollback() // 回滚事务log.Fatal(err)}_, err = tx.Exec("UPDATE accounts SET balance = balance - 50 WHERE id = ?", 1) // 执行操作if err != nil { // 如果执行失败,则回滚事务并输出错误信息并退出程序tx.Rollback() // 回滚事务log.Fatal(err)}// 提交事务err = tx.Commit()if err != nil { // 如果提交失败,则回滚事务并输出错误信息并退出程序log.Fatal(err)}fmt.Println("Transaction completed successfully")
}

3 事务的隔离级别

MySQL 提供四种事务隔离级别:

  1. 读未提交(Read Uncommitted):事务可以读取其他未提交事务的数据,可能导致脏读。
  2. 读已提交(Read Committed):事务只能读取已提交的数据,避免脏读。
  3. 可重复读(Repeatable Read):同一事务中多次读取数据一致,避免不可重复读(MySQL 默认级别)。
  4. 可串行化(Serializable):完全隔离,事务逐一执行,避免幻读。

设置事务隔离级别

SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;

在 Golang 中可以通过 SET 语句设置隔离级别:

tx.Exec("SET TRANSACTION ISOLATION LEVEL SERIALIZABLE")

4、并发访问的最佳实践

在并发环境中,事务之间可能发生以下冲突:

  • 脏读(Dirty Read):一个事务读取了另一个未提交事务的数据。
  • 不可重复读(Non-repeatable Read):同一事务中多次读取结果不一致。
  • 幻读(Phantom Read):事务中新增或删除的记录导致结果变化。

以下是一些避免冲突的最佳实践:

5、使用事务管理并发

事务能够隔离并发访问,保证数据一致性。结合合适的隔离级别,可以避免大多数并发问题。

使用锁机制

行锁和表锁

  • 行锁:锁定某一行数据,适用于高并发场景。
  • 表锁:锁定整个表,避免全表修改导致的数据不一致问题。

LOCK TABLES accounts WRITE;UPDATE accounts SET balance = balance - 50 WHERE id = 1;

UNLOCK TABLES;

在 Golang 中,利用事务和条件语句实现锁机制:

tx.Exec("SELECT * FROM accounts WHERE id = ? FOR UPDATE", 1)

6、乐观锁与悲观锁

6.1 乐观锁

乐观锁通过检查版本号或时间戳,确保并发访问不会冲突。

乐观锁实现

SELECT version FROM accounts WHERE id = 1;UPDATE accounts SET balance = balance - 50, version = version + 1 WHERE id = 1 AND version = ?;

Golang 示例

func updateBalanceWithOptimisticLock(db *sql.DB, id int, amount int) error { // 乐观锁var version interr := db.QueryRow("SELECT version FROM accounts WHERE id = ?", id).Scan(&version) // 查询账户的版本号if err != nil { // 如果查询失败,则输出错误信息并退出程序return err}result, err := db.Exec("UPDATE accounts SET balance = balance - ?, version = version + 1 WHERE id = ? AND version = ?", amount, id, version) // 更新账户的余额和版本号if err != nil {return err}rowsAffected, err := result.RowsAffected() // 获取受影响的行数if err != nil {return err}if rowsAffected == 0 { // 如果受影响的行数为0,则说明版本号不一致,返回错误信息return fmt.Errorf("transaction conflict, try again")}return nil
}

6.2 悲观锁

悲观锁假定冲突必然发生,在操作前加锁以避免冲突。

悲观锁实现

SELECT * FROM accounts WHERE id = 1 FOR UPDATE;

Golang 示例

func updateBalanceWithPessimisticLock(db *sql.DB, id int, amount int) error { // 悲观锁tx, err := db.Begin() // 开启事务if err != nil { // 如果开启事务失败,则输出错误信息并退出程序return err}_, err = tx.Exec("SELECT * FROM accounts WHERE id = ? FOR UPDATE", id) // 使用FOR UPDATE关键字锁定记录if err != nil { // 如果锁定记录失败,则输出错误信息并退出程序tx.Rollback() // 回滚事务return err}_, err = tx.Exec("UPDATE accounts SET balance = balance - ? WHERE id = ?", amount, id) // 更新账户的余额if err != nil { // 如果更新记录失败,则回滚事务并返回错误tx.Rollback() // 回滚事务return err}return tx.Commit() // 提交事务并返回nil
}

7、应用场景

  • 电子商务系统:订单支付与库存扣减必须确保一致性。
  • 银行转账系统:多账户之间的资金流转需要事务保证。
  • 多人协作编辑系统:利用乐观锁解决并发编辑冲突。

http://www.ppmy.cn/news/1564382.html

相关文章

学英语学技术:Elasticsearch 线程池

单词 汉语意思 音标 allocate 分配 /ˈləˌkeɪt/ coordination 协调 /koʊˌɔːrdɪˈneɪʃn/ deprecated 废弃的 /ˈdɛprəˌkeɪtɪd/ elasticsearch 弹性搜索(专有名词) /ˌɛlɪkˈsɜːrtʃ/ execute 执行 /ˈɛksɪˌkjuːt…

C 语言的void*到底是什么?

一、void* 的类型任意性 void* 是一种通用指针类型。它可以指向任意类型的数据。例如,它可以指向一个整数(int)、一个浮点数(float)、一个字符(char)或者一个结构体等。在C语言中,当…

电脑换固态硬盘

参考: https://baijiahao.baidu.com/s?id1724377623311611247 一、根据尺寸和缺口可以分为以下几种: 1、M.2 NVME协议的固态 大部分笔记本是22x42MM和22x80MM nvme固态。 在京东直接搜: M.2 2242 M.2 2280 2、msata接口固态 3、NGFF M.…

Word中如何格式化与网页和 HTML 内容相关的元素

在 Microsoft Word 中,HTML变量、HTML打字机、HTML地址、HTML定义、HTML键盘、HTML缩写、HTML样本、HTML引文 等样式是针对在文档中处理与 HTML 相关内容时,方便格式化特定类型的文本元素。以下是每个样式的详细说明及其使用场景: 1. HTML 变…

“提升大语言模型推理与规划能力的策略:思维链提示与由少至多提示”

思维链提示(Chain-of-Thought Prompting)和由少至多提示(Least-to-Most Prompting)是两种提升大语言模型在推理和规划任务上表现的有效方法。下面详细介绍这两种方法的原理和应用: 思维链提示(Chain-of-Th…

模板 进阶

博客ID:LanFuRen C系列专栏:C语言重点部分 C语言注意点 C基础 Linux 数据结构 C注意点 今日好题 声明等级:黑色->蓝色->红色 欢迎新粉加入,会一直努力提供更优质的编程博客,希望大家三连支持一下啦 目录 1…

MES设备日志采集工具

永久免费: <下载> <使用说明> 用途 定时全量或增量采集工控机,电脑文件或日志. 优势 开箱即用: 解压直接运行.不需额外下载.管理设备: 后台统一管理客户端.无人值守: 客户端自启动,自更新.稳定安全: 架构简单,兼容性好,通过授权控制访问. 架构 技术架构: Asp…

TCP Window Full是怎么来的

wireshark查看包时&#xff0c;会看到TCP Window Full&#xff0c;总结下它的特点&#xff1a; 1. Sender会显示 TCP Window Full 2. “Sender已发出&#xff0c;但&#xff0c;Receiver尚未ack的字节”&#xff0c;即Sender的 bytes in flights 3. Sender的 bytes in fligh…