当前位置: 首页 > article >正文

mysql中与并发相关的问题?

今天我们来聊聊 MySQL 中与并发相关的一些问题。作为一名资深 Python 开发工程师,我觉得这些问题不仅关乎数据库的稳定性和数据的一致性,更与我们的代码实现和业务逻辑密切相关。

尤其是在高并发环境下,如何保证数据的一致性,如何防止脏读、不可重复读和幻读等问题,真的是每个程序员都必须知道的内容。今天就通过一些简单的代码示例和场景说明,让大家更加直观地理解这些问题。

首先,我们知道 MySQL 服务端支持多个客户端同时连接,这就意味着 MySQL 会在多个事务并发执行时进行资源的竞争和调度。

而并发执行的事务中,如果没有妥善的控制,就可能会遇到一些数据一致性问题。常见的这些问题包括脏读(Dirty Read)、不可重复读(Non-repeatable Read)和幻读(Phantom Read)。那么,具体它们是怎么发生的呢?

脏读(Dirty Read)

脏读是指一个事务读到了另一个事务未提交的脏数据。如果事务 A 修改了数据,但事务 A 并没有提交,而事务 B 读取到了事务 A 修改后的数据,那就产生了脏读。如果事务 A 随后回滚了,那么事务 B 得到的数据就是无效的。这种现象就叫做脏读。

图片

举个例子,假设我们有两个事务 A 和 B,事务 A 从数据库中读取了小林的余额数据,然后进行了一次修改,但没有提交。与此同时,事务 B 也读取了小林的余额数据,这时事务 B 看到的余额已经是事务 A 修改后的数据了,即便事务 A 最终回滚了。

-- 事务 A
START TRANSACTION;
UPDATE account SET balance = balance - 100 WHERE name = '小林';
-- 事务 A 没有提交

-- 事务 B
START TRANSACTION;
SELECT balance FROM account WHERE name = '小林';  -- 读到了事务 A 更新后的数据
-- 事务 B 没有更新数据,但可能会被脏数据影响

如果事务 A 最后执行回滚,那么事务 B 得到的数据就是过期数据,这就属于脏读。

如何避免脏读?
可以通过设置事务隔离级别来避免脏读,常用的隔离级别是 READ COMMITTED。在这个隔离级别下,事务 B 不会读取未提交的数据,从而避免了脏读的发生。

不可重复读(Non-repeatable Read)

不可重复读是指在同一个事务中,多次读取同一数据时,如果中间有其他事务修改了数据,就会导致前后两次读取的结果不一致。

图片

举个简单的例子,事务 A 从数据库中读取了小林的余额数据并进行了一些处理,接着事务 B 修改了余额并提交了。等事务 A 再次读取余额时,看到的数据就和第一次不一样了,这就是不可重复读。

-- 事务 A
START TRANSACTION;
SELECT balance FROM account WHERE name = '小林';  -- 读到 1000 元
-- 假设事务 A 在处理中

-- 事务 B
START TRANSACTION;
UPDATE account SET balance = balance + 500 WHERE name = '小林';  -- 修改余额
COMMIT;

-- 事务 A 再次读取
SELECT balance FROM account WHERE name = '小林';  -- 读到 1500 元

在上面的代码中,事务 A 在第一次读取时读到的是 1000 元,而在第二次读取时,却读到了 1500 元。造成这种现象的原因是事务 B 在事务 A 执行过程中对数据进行了修改,导致了数据不一致。

如何避免不可重复读?

可以通过使用更高的事务隔离级别来避免,比如 REPEATABLE READ。在这个隔离级别下,事务 A 不会因为其他事务的提交而看到不一致的数据。

幻读(Phantom Read)

幻读是指在同一个事务中,重复执行相同的查询时,查询结果的数量发生了变化。这种问题通常发生在对数据行进行插入、删除、更新等操作时。

如果在事务 A 查询某个条件下的记录时,事务 B 在事务 A 执行查询的过程中插入了符合条件的新记录,那么事务 A 进行第二次查询时就会看到额外的记录,从而产生幻读现象。

图片

举个例子,假设事务 A 查询余额大于 100 万的所有账户,得到了 5 条记录,然后事务 B 插入了一条新的余额大于 100 万的记录,提交事务后,事务 A 再次查询时发现记录数量变成了 6 条。这样就出现了幻读。

-- 事务 A
START TRANSACTION;
SELECT COUNT(*) FROM account WHERE balance > 1000000;  -- 查询到 5 条记录
-- 假设事务 A 在处理中

-- 事务 B
START TRANSACTION;
INSERT INTO account (name, balance) VALUES ('小张', 2000000);  -- 插入一条新记录
COMMIT;

-- 事务 A 再次查询
SELECT COUNT(*) FROM account WHERE balance > 1000000;  -- 查询到 6 条记录

在这个例子中,事务 A 在第一次查询时得到了 5 条记录,而在第二次查询时却得到了 6 条记录,产生了幻读现象。

如何避免幻读?
为了避免幻读问题,可以使用 SERIALIZABLE 隔离级别,它会强制事务之间的互斥,确保在一个事务运行期间不会有其他事务修改或插入数据,从而避免了幻读。

如何在 MySQL 中设置隔离级别?

MySQL 提供了不同的事务隔离级别,每种隔离级别可以有效地解决某些并发问题。常用的隔离级别有以下几种:

  1. READ UNCOMMITTED:最低的隔离级别,允许脏读。

  2. READ COMMITTED:防止脏读,但允许不可重复读和幻读。

  3. REPEATABLE READ:防止脏读和不可重复读,但允许幻读(MySQL 默认使用此级别)。

  4. SERIALIZABLE:最高的隔离级别,防止所有并发问题,但性能可能较差。

我们可以通过以下语句来设置事务隔离级别:

-- 设置隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;

如果面试官问你:如何理解 MySQL 的事务隔离级别?

你的回答:

MySQL 提供了四种常见的事务隔离级别,每个级别的作用和影响如下:

  1. READ UNCOMMITTED:事务可以读取其他事务未提交的数据,可能出现脏读、不可重复读和幻读问题。

  2. READ COMMITTED:事务只能读取已提交的数据,避免了脏读问题,但仍可能发生不可重复读和幻读。

  3. REPEATABLE READ:事务在整个生命周期内读取的数据是固定的,避免了脏读和不可重复读问题,但在 MySQL 中,仍然可能出现幻读。

  4. SERIALIZABLE:事务完全串行化,避免了脏读、不可重复读和幻读问题,但会大幅影响性能。

在实际开发中,根据业务的需求选择合适的事务隔离级别非常重要。例如,如果对数据一致性要求非常高,可以选择 SERIALIZABLE,但如果对性能要求较高,可能会选择 REPEATABLE READREAD COMMITTED


http://www.kler.cn/a/444146.html

相关文章:

  • Restaurants WebAPI(三)——Serilog/
  • 开源轮子 - Logback 和 Slf4j
  • 电商数据流通的未来:API接口的智能化与自动化趋势
  • 多个Echart遍历生成 / 词图云
  • 未来趋势系列 篇五:自主可控科技题材解析和股票梳理
  • 多屏幕编程时用pygame指定窗口出现在第二块显示器上的方法
  • matlab的一些时间函数【转】
  • AGM FPGA如何配置上拉或者下拉电阻
  • 按照字幕拆解视频实战
  • SSH连接成功,但VSCode连接不成功
  • DALSA工业相机SDK二次开发(图像采集及保存)C#版
  • 智慧社区系统源码社区服务软件家政跑腿月嫂保洁维修小程序
  • 企业如何选择媒体发稿平台及相关事项?媒介盒子分享
  • maven权威指南(读书笔记一)
  • 「iOS」通过CoreLocation Framework深入了解MVC架构
  • 硬件---14---PCB学习:PCB封装库及布局操作
  • Linux Red Hat安装包安装nodejs
  • samout llm解码 幻觉更低更稳定
  • CentOS 快捷安装 jenkins 并设置开机自启
  • vue相关的---Vuex
  • 游戏AI实现-寻路算法(DFS)
  • ESP-AT 固件:物联网智能 “引擎”
  • C语言学习day22:URLDownloadToFile函数/开发文件下载工具
  • [python]使用flask-caching缓存数据
  • QT图形/视图架构详解(二)
  • Oracle 技术精选学习