在MySQL中处理同时进行的SELECT和UPDATE操作20240729

news2024/9/9 5:24:27

在MySQL中处理同时进行的SELECT和UPDATE操作

在MySQL中同时对同一张表执行SELECT和UPDATE操作可能会引发脏读、不可重复读和幻读等问题。本文将详细介绍这些问题及其解决方法,并提供具体的示例代码。

问题描述

在多事务环境下,可能会遇到以下问题:

  1. 脏读 (Dirty Read): 一个事务在读取数据的同时,另一个事务修改了同一行的数据并提交,第一个事务读取的数据可能是不一致的。
  2. 不可重复读 (Non-repeatable Read): 一个事务在读取数据的同时,另一个事务修改了同一行的数据并提交,第一个事务再次读取同一行的数据时,可能会得到不同的结果。
  3. 幻读 (Phantom Read): 一个事务在读取数据的同时,另一个事务插入了符合第一个事务查询条件的新数据并提交,第一个事务再次执行相同的查询时,会发现存在新的数据。

解决方法

为了解决上述问题,可以使用MySQL提供的事务隔离级别和锁机制来确保数据的一致性。

事务隔离级别

MySQL支持四种事务隔离级别:

  1. READ UNCOMMITTED: 允许脏读、不可重复读和幻读。
  2. READ COMMITTED: 防止脏读,但允许不可重复读和幻读。
  3. REPEATABLE READ: 防止脏读和不可重复读,但允许幻读。
  4. SERIALIZABLE: 防止脏读、不可重复读和幻读。

可以通过设置事务隔离级别来控制并发事务的行为。下面是设置事务隔离级别的示例:

-- 设置事务隔离级别为 REPEATABLE READ
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;

START TRANSACTION;

-- 你的查询和更新操作
SELECT * FROM employees WHERE id = 1 FOR UPDATE;
UPDATE employees SET name = 'new_name' WHERE id = 1;

COMMIT;
锁机制

MySQL的InnoDB存储引擎提供了行级锁,可以在查询时使用锁来确保数据的一致性。

-- 设置事务隔离级别为 SERIALIZABLE
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

START TRANSACTION;

-- 你的查询和更新操作
SELECT * FROM employees WHERE id = 1 FOR UPDATE;
UPDATE employees SET name = 'new_name' WHERE id = 1;

COMMIT;

在上述示例中,使用FOR UPDATE语句对查询的行加锁,以防止其他事务在当前事务完成之前修改这些行。

实际案例分析

假设我们在两个不同的数据库实例中进行实验,以验证不同配置下MySQL的行为。

表结构

假设我们有一个名为employees的表,表结构如下:

CREATE TABLE employees (
    id INT PRIMARY KEY,
    name VARCHAR(255),
    department VARCHAR(255)
);
实验步骤和SQL语句
  1. 环境准备
    • 创建数据库和表。
    • 插入初始数据。
-- 创建数据库
CREATE DATABASE company_db;

-- 选择数据库
USE company_db;

-- 创建表
CREATE TABLE employees (
    id INT PRIMARY KEY,
    name VARCHAR(255),
    department VARCHAR(255)
);

-- 插入初始数据
INSERT INTO employees (id, name, department) VALUES (1, 'Alice', 'HR');
  1. Session 1: 开始事务,查询employees表。
-- Session 1
START TRANSACTION;
SELECT * FROM employees;
  1. Session 2: 插入一行数据到employees表并提交事务。
-- Session 2
START TRANSACTION;
INSERT INTO employees (id, name, department) VALUES (2, 'Bob', 'Engineering');
COMMIT;
  1. Session 1: 再次查询employees表。
-- Session 1
SELECT * FROM employees;

实验现象

在两个环境中进行上述操作,并观察结果。

  • 环境A: Session 1无法查询到Session 2提交的数据。
  • 环境B: Session 1可以查询到Session 2提交的数据。

实际案例分析

通过不同的环境验证,我们发现:

环境A
  • MySQL版本: 5.7.13
  • autocommit: OFF
  • 事务隔离级别: REPEATABLE READ

在此环境中,Session 1在第二次查询时无法看到Session 2提交的数据,因为事务在开始时就固定了读取的数据版本。

环境B
  • MySQL版本: 5.7.13
  • autocommit: ON
  • 事务隔离级别: REPEATABLE READ

在此环境中,Session 1在第二次查询时可以看到Session 2提交的数据,因为每个查询都是一个新的事务。

验证问题

通过不同组合的autocommit和隔离级别参数,验证查询结果是否符合预期。

验证组合
  1. autocommit=ON,隔离级别=REPEATABLE-READ: Session 1能否在T3时刻看到Session 2提交的数据?
    • 答:能。
  2. autocommit=OFF,隔离级别=REPEATABLE-READ: Session 1能否在T3时刻看到Session 2提交的数据?
    • 答:不能。
  3. autocommit=ON,隔离级别=READ-COMMITTED: Session 1能否在T3时刻看到Session 2提交的数据?
    • 答:能。
  4. autocommit=OFF,隔离级别=READ-COMMITTED: Session 1能否在T3时刻看到Session 2提交的数据?
    • 答:能。

代码示例

以下是Python、C语言和Go语言的示例代码,演示如何在MySQL中使用事务和锁机制来解决SELECT和UPDATE操作的并发问题。

Python代码示例
import pymysql

# 连接数据库
conn1 = pymysql.connect(host='localhost', user='root', password='password', db='company_db', autocommit=False)
conn2 = pymysql.connect(host='localhost', user='root', password='password', db='company_db', autocommit=True)

try:
    with conn1.cursor() as cursor1, conn2.cursor() as cursor2:
        # Session 1: 开始事务并查询数据
        cursor1.execute("START TRANSACTION;")
        cursor1.execute("SELECT * FROM employees;")
        result1 = cursor1.fetchall()
        print("Session 1 - First Query:", result1)

        # Session 2: 插入数据并提交事务
        cursor2.execute("INSERT INTO employees (id, name, department) VALUES (2, 'Bob', 'Engineering');")
        cursor2.execute("COMMIT;")

        # Session 1: 再次查询数据
        cursor1.execute("SELECT * FROM employees;")
        result2 = cursor1.fetchall()
        print("Session 1 - Second Query:", result2)
finally:
    conn1.close()
    conn2.close()
C语言代码示例
#include <mysql/mysql.h>
#include <stdio.h>

void finish_with_error(MYSQL *con) {
    fprintf(stderr, "%s\n", mysql_error(con));
    mysql_close(con);
    exit(1);
}

int main() {
    MYSQL *con1 = mysql_init(NULL);
    MYSQL *con2 = mysql_init(NULL);

    if (con1 == NULL || con2 == NULL) {
        fprintf(stderr, "mysql_init() failed\n");
        exit(1);
    }

    if (mysql_real_connect(con1, "localhost", "root", "password", "company_db", 0, NULL, 0) == NULL ||
        mysql_real_connect(con2, "localhost", "root", "password", "company_db", 0, NULL, 0) == NULL) {
        finish_with_error(con1);
        finish_with_error(con2);
    }

    // Session 1: 开始事务并查询数据
    if (mysql_query(con1, "START TRANSACTION") ||
        mysql_query(con1, "SELECT * FROM employees")) {
        finish_with_error(con1);
    }

    MYSQL_RES *result1 = mysql_store_result(con1);
    if (result1 == NULL) {
        finish_with_error(con1);
    }

    MYSQL_ROW row;
    printf("Session 1 - First Query:\n");
    while ((row = mysql_fetch_row(result1))) {
        printf("%s %s %s\n", row[0], row[1], row[2]);
    }
    mysql_free_result(result1);

   

 // Session 2: 插入数据并提交事务
    if (mysql_query(con2, "START TRANSACTION") ||
        mysql_query(con2, "INSERT INTO employees (id, name, department) VALUES (2, 'Bob', 'Engineering')") ||
        mysql_query(con2, "COMMIT")) {
        finish_with_error(con2);
    }

    // Session 1: 再次查询数据
    if (mysql_query(con1, "SELECT * FROM employees")) {
        finish_with_error(con1);
    }

    MYSQL_RES *result2 = mysql_store_result(con1);
    if (result2 == NULL) {
        finish_with_error(con1);
    }

    printf("Session 1 - Second Query:\n");
    while ((row = mysql_fetch_row(result2))) {
        printf("%s %s %s\n", row[0], row[1], row[2]);
    }
    mysql_free_result(result2);

    mysql_close(con1);
    mysql_close(con2);

    return 0;
}
Go语言代码示例
package main

import (
    "database/sql"
    "fmt"
    _ "github.com/go-sql-driver/mysql"
)

func main() {
    db1, err := sql.Open("mysql", "root:password@tcp(localhost:3306)/company_db?autocommit=false")
    if err != nil {
        panic(err)
    }
    defer db1.Close()

    db2, err := sql.Open("mysql", "root:password@tcp(localhost:3306)/company_db?autocommit=true")
    if err != nil {
        panic(err)
    }
    defer db2.Close()

    // Session 1: 开始事务并查询数据
    tx1, err := db1.Begin()
    if err != nil {
        panic(err)
    }

    rows1, err := tx1.Query("SELECT * FROM employees")
    if err != nil {
        panic(err)
    }
    defer rows1.Close()

    fmt.Println("Session 1 - First Query:")
    for rows1.Next() {
        var id int
        var name, department string
        err = rows1.Scan(&id, &name, &department)
        if err != nil {
            panic(err)
        }
        fmt.Println(id, name, department)
    }

    // Session 2: 插入数据并提交事务
    tx2, err := db2.Begin()
    if err != nil {
        panic(err)
    }

    _, err = tx2.Exec("INSERT INTO employees (id, name, department) VALUES (?, ?, ?)", 2, "Bob", "Engineering")
    if err != nil {
        panic(err)
    }

    err = tx2.Commit()
    if err != nil {
        panic(err)
    }

    // Session 1: 再次查询数据
    rows2, err := tx1.Query("SELECT * FROM employees")
    if err != nil {
        panic(err)
    }
    defer rows2.Close()

    fmt.Println("Session 1 - Second Query:")
    for rows2.Next() {
        var id int
        var name, department string
        err = rows2.Scan(&id, &name, &department)
        if err != nil {
            panic(err)
        }
        fmt.Println(id, name, department)
    }

    tx1.Commit()
}

总结

通过设置合适的事务隔离级别和使用行级锁,可以有效地解决在MySQL中同时执行SELECT和UPDATE操作同一张表时遇到的脏读、不可重复读和幻读问题。这不仅确保了数据的一致性,还提高了系统的并发处理能力。理解并正确使用这些机制,是保证数据库操作正确性和效率的关键。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1961633.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

6 Java的基本程序设计结构(基本语法5)- 面向对象进阶

文章目录 面向对象进阶一、 static 静态1 静态变量(1)基本定义和用法(2)静态变量内存图2 静态方法(1)基本定义和用法(2)工具类练习:按下面需求写一个工具类3 static注意事项4 重新认识main方法二、继承1 继承的概念2 继承的特点3 继承到底能继承父类中的哪些内容?4 继…

leetcode日记(63)颜色分类

感觉就是排序问题&#xff1f;我使用的是时间复杂度比较高的简单粗暴排序法&#xff0c;时间复杂度O&#xff08;n^2&#xff09;。 class Solution { public:void sortColors(vector<int>& nums) {int nnums.size();for(int i0;i<n;i){for(int ji1;j<n;j){if…

泛微OA BPM 全程数字化业务介绍、管理、财务一体化 数据业务架构图 上帝视角 02

III.泛微业务、管理、财务一体化过程介绍 IV.低代码平台及典型场景搭建过程 V.全程数字化运营平台价值总结 档案管理 档案接收,四性检测,快速可查找 重要:档案管理:架构总图 业务应用都在一个平台,确保档案实现100%归档 自动化档案采集:自动接收各类档案,如文书档案、合…

速通JS模块化规范

目录 1模块化概述 1.1什么是模块化&#xff1f; 1.2为什么需要模块化&#xff1f; 2有哪些模块化规范&#xff1f; 3导入与导出的概念 4CommonJS 规范 4.1初步体验 4.2导出数据 4.3导入数据 4.4扩展理解 4.5浏览器端运行 5ES6 模块化规范 5.1初步体验 5.2Node 中运…

操作系统课程设计:(JAVA)进程管理系统(附源码zip,jdk11,IDEA Ultimate2024 )

一.题目要求描述 本设计的目的是加深对进程概念及进程管理各部分内容的理解&#xff1b;熟悉进程管理中主要数据结构的设计及进程调度算法、进程控制机构、同步机构及通讯机构的实施。要求设计一个允许n个进程并发运行的进程管理模拟系统。 该系统包括有简单的进程控制、同步与…

一行代码教你使用Python制作炫酷二维码

二维码&#xff0c;我们日常生活中随处可见的编码方式&#xff0c;凭借其方便快捷的信息承载能力&#xff0c;已经渗透到各行各业。 MyQR 的介绍 MyQR 是一个 Python 库&#xff0c;用于生成自定义二维码&#xff0c;包括带有 Logo、彩色和动态的二维码。它基于 Python 的 qr…

【基础篇】Docker 镜像管理 THREE

嘿&#xff0c;小伙伴们&#xff01;我是小竹笋&#xff0c;一名热爱创作的工程师。在上一篇文章中&#xff0c;我们探讨了 Docker 的架构与关键组件。今天&#xff0c;让我们一起深入了解一下 Docker 镜像管理的相关知识吧&#xff01; &#x1f4e6; 创建和管理镜像 镜像是…

Qt程序移植至Arm开发板

环境准备&#xff1a; 系统调试工具SecureCRT SecureCRT 是一款支持 SSH 协议的终端仿真软件&#xff0c;可通过串口或网口对评估板系统信息进行查看、对评估板系统进行调试等。 SecureCRT的安装破解 [详细过程2024]-CSDN博客https://blog.csdn.net/2301_76659937/article/det…

2-50 基于matlab的遗传模拟退火算法的聚类算法

基于matlab的遗传模拟退火算法的聚类算法&#xff0c;以模糊K-均值聚类算法为基础&#xff0c;对各样本的聚类中心进行优化&#xff0c;输出聚类可视化结果。聚类类别数可自由输入。程序已调通&#xff0c;可直接运行。 2-50 遗传模拟退火算法的聚类算法 - 小红书 (xiaohongshu…

【源码阅读】Redisson lock源码

Redisson 加锁非常简单&#xff0c;还支持 redis 单实例、redis 哨兵、redis cluster、redis master-slave 等各种部署架构 RLock lock redisson.getLock("cyk-test"); lock.lock(); lock.unlock(); 底层原理 加锁机制 废话不多说&#xff0c;直接看源码&#xf…

Go语言----flag包(导入、配置、以及常用方法Parse()、Parsed()、NArg())

在 Go语言中有很多种方法来处理命令行参数。如果我们只是想简单的获取命令行的参数&#xff0c;可以像Go语言–延迟调用defer、获取命令行参数、局部变量以及全局变量中介绍的不使用任何库&#xff0c;直接使用 os.Args&#xff1b; d但是 Golang 的标准库提供了 flag 包来处理…

机械拆装-基于Unity-本地数据持久化

目录 1. 数据结构简介&#xff1a;数据的集合 1.1 线性数据结构 1.2 非线性数据结构 2. 对数据集合的操作&#xff1a; 3. 数据持久化 3.1 数据的序列化存储 3.2 JSON文件硬盘存储 3.2.1 Json文件允许存储的数据类型 3.2.2 Json文件的语法格式 3.2.3 Json文件的读取 3.2.4 …

Echarts toolbox相关配置 dataZoom缩放

前言:最近开发遇到一个echarts相关问题,需要实现用户鼠标滚动实现图表缩放,或者实现选中某一段区域进行缩放,放大效果; 1.第一个需求就是区域缩放按钮要隐藏掉,用户鼠标放在图表内就默认实现选择效果,并且区域缩放还原按钮不能隐藏,需要在初始化配置这三个属性. // 假设你已经…

孙宇晨建议中国重新考虑“比特币政策”!中美竞争将使加密货币行业受益?美国对“中国崛起”感到焦虑!

近日&#xff0c;前美国总统特朗普发表了一番振奋人心的比特币演讲&#xff0c;令加密货币社群反响热烈。而Tron区块链创始人孙宇晨则建议中国重新考虑其对于比特币的政策立场&#xff0c;并指出中美两国在加密货币领域的竞争&#xff0c;将使整个行业受益。这再次引发了人们对…

未来社交:Facebook如何定义虚拟现实的新时代?

随着科技的飞速发展&#xff0c;虚拟现实&#xff08;VR&#xff09;逐渐从科幻小说中的幻想变成了现实生活中的前沿技术。在这一领域&#xff0c;Facebook&#xff08;现已更名为Meta&#xff09;扮演了重要角色&#xff0c;通过不断的创新和投资&#xff0c;致力于打造一个全…

花几千上万学习Java,真没必要!(三十六)

1、File类&#xff1a; 测试代码1&#xff1a; package filetest.com; import java.io.File; import java.io.IOException; public class FileOperations { public static void main(String[] args) { // 创建新文件File file new File("example.txt"); tr…

18966 两两配对差值最小

这个问题可以通过排序和配对来解决。首先&#xff0c;我们将数组排序&#xff0c;然后我们将数组的第一个元素和最后一个元素配对&#xff0c;第二个元素和倒数第二个元素配对&#xff0c;以此类推。这样&#xff0c;我们可以得到n/2个和&#xff0c;然后我们找出这些和中的最大…

光伏可行性研究报告能否自动生成?

随着技术的不断进步和应用的广泛普及&#xff0c;光伏项目的规划与实施也面临着更加复杂多变的考量因素&#xff0c;其中&#xff0c;光伏可行性研究报告成为了项目前期不可或缺的重要内容。那么&#xff0c;面对这一需求&#xff0c;光伏可行性研究报告能否实现自动生成呢&…

Nat Med·UNI:开启计算病理学新篇章的自监督基础模型|顶刊精析·24-07-31

小罗碎碎念 本期推文主题 这一期推文是病理AI基础模型UNI的详细介绍&#xff0c;原文如下。下期推文会介绍如何使用这个模型&#xff0c;为了你能看懂下期的推文&#xff0c;强烈建议你好好看看今天这期推文。 看完这篇推文以后&#xff0c;你大概就能清楚这个模型对自己的数据…

搞懂数据结构与Java实现

文章链接&#xff1a;搞懂数据结构与Java实现 (qq.com) 代码链接&#xff1a; Java实现数组模拟循环队列代码 (qq.com) Java实现数组模拟栈代码 (qq.com) Java实现链表代码 (qq.com) Java实现哈希表代码 (qq.com) Java实现二叉树代码 (qq.com) Java实现图代码 (qq.com)