首页 > 其他分享 >Select xxx for update

Select xxx for update

时间:2024-07-04 22:20:32浏览次数:18  
标签:status ... 事务 xxx update UPDATE SELECT id Select

一. Mysql 数据库 的 RR 隔离级别下, 如果在事务中使用SELECT ... FOR UPDATE, 实现如下伪代码所描述的效果:

begin transaction
select id from t where status = false limit 1,1;
if select result is not empty;
then update status = true where id = ;
commit;

那么能实现吗? 答案是肯定的。

其实在MySQL数据库的可重复读(Repeatable Read,RR)隔离级别下,通过事务使用SELECT ... FOR UPDATE可以实现伪代码所描述的效果。以下是详细说明和实现步骤:

伪代码描述

BEGIN;
SELECT id FROM t WHERE status = false LIMIT 1 FOR UPDATE;
IF (SELECT result is not empty) THEN
    UPDATE t SET status = true WHERE id = <the selected id>;
COMMIT;

实现步骤

在MySQL中,可重复读(RR)隔离级别可以防止幻读、脏读和不可重复读。在RR隔离级别下,使用SELECT ... FOR UPDATE可以确保选中的行在事务提交前不会被其他事务修改。

SQL 实现

  1. 开启事务

    START TRANSACTION;
    
  2. 选择并锁定符合条件的行

    SELECT id FROM t WHERE status = false LIMIT 1 FOR UPDATE;
    
  3. 检查查询结果并更新

    • 在编程语言中(如Java、Python等)处理查询结果,如果结果不为空,则进行更新操作。
    • 假设使用的是Java,示例代码如下:

Java示例

假设我们使用JDBC来实现上述逻辑,以下是一个示例代码:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class MySQLTransactionExample {
    public static void main(String[] args) {
        String url = "jdbc:mysql://localhost:3306/yourdatabase";
        String user = "yourusername";
        String password = "yourpassword";

        Connection conn = null;
        PreparedStatement selectStmt = null;
        PreparedStatement updateStmt = null;
        ResultSet rs = null;

        try {
            // 获取数据库连接
            conn = DriverManager.getConnection(url, user, password);
            conn.setAutoCommit(false); // 开启事务

            // 执行SELECT ... FOR UPDATE
            String selectSQL = "SELECT id FROM t WHERE status = false LIMIT 1 FOR UPDATE";
            selectStmt = conn.prepareStatement(selectSQL);
            rs = selectStmt.executeQuery();

            if (rs.next()) {
                int id = rs.getInt("id");

                // 如果查询结果不为空,执行UPDATE操作
                String updateSQL = "UPDATE t SET status = true WHERE id = ?";
                updateStmt = conn.prepareStatement(updateSQL);
                updateStmt.setInt(1, id);
                updateStmt.executeUpdate();

                // 提交事务
                conn.commit();
                System.out.println("Transaction committed. Row updated with id: " + id);
            } else {
                System.out.println("No rows found with status = false.");
                conn.rollback(); // 没有行需要更新,回滚事务
            }
        } catch (SQLException e) {
            e.printStackTrace();
            if (conn != null) {
                try {
                    conn.rollback(); // 出现异常时回滚事务
                } catch (SQLException ex) {
                    ex.printStackTrace();
                }
            }
        } finally {
            // 关闭资源
            try {
                if (rs != null) rs.close();
                if (selectStmt != null) selectStmt.close();
                if (updateStmt != null) updateStmt.close();
                if (conn != null) conn.close();
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
        }
    }
}

关键点

  1. 事务

    • 使用 START TRANSACTION 开启事务。
    • 在更新成功后使用 COMMIT 提交事务;如果没有要更新的行或出现异常,则使用 ROLLBACK 回滚事务。
  2. 行锁

    • 使用 SELECT ... FOR UPDATE 语句锁定选中的行,防止其他事务在当前事务提交前修改这些行。
  3. 处理结果

    • 在编程逻辑中处理查询结果。如果查询返回不为空,则执行更新操作。
  4. 错误处理

    • 在出现异常时,确保回滚事务以保持数据一致性。

通过这种方式,可以确保在高并发环境下,只会有一个事务成功更新满足条件的行,从而避免竞争条件和数据不一致的问题。


二. 那么SELECT ... FOR UPDATE 语句 在MySql 中是加的读锁还是写锁呢?

在MySQL中,SELECT ... FOR UPDATE 语句会为选中的行加上写锁(也称为排他锁或X锁),而不是读锁。写锁可以防止其他事务在当前事务提交之前读取或修改这些行,从而确保数据的一致性和正确性。

写锁的作用

  • 防止并发修改:当一个事务使用SELECT ... FOR UPDATE选中行并加上写锁后,其他事务不能对这些行进行任何更新操作,直到当前事务完成(提交或回滚)。
  • 防止读取:在MySQL的默认InnoDB存储引擎下,SELECT ... FOR UPDATE选中的行也不能被其他事务读取(即使是读锁,也不能加在这些行上),确保当前事务的独占性。

示例

假设有一张名为 orders 的表,包含以下列:idstatusamount

开启事务并加锁

START TRANSACTION;

SELECT id FROM orders WHERE status = 'pending' LIMIT 1 FOR UPDATE;

在上面的SQL语句中,符合条件的行会被加上写锁。其他事务在当前事务完成之前无法修改或读取这些行。

更新选中的行

UPDATE orders SET status = 'completed' WHERE id = <selected_id>;

COMMIT;

举例说明并发控制

假设有两个事务T1和T2同时运行:

事务T1:

START TRANSACTION;

SELECT id FROM orders WHERE status = 'pending' LIMIT 1 FOR UPDATE;  -- 假设选中了id为1的行
-- id 为 1 的行被锁定,其他事务无法读取或修改

UPDATE orders SET status = 'completed' WHERE id = 1;

COMMIT;

事务T2:

START TRANSACTION;

SELECT id FROM orders WHERE status = 'pending' LIMIT 1 FOR UPDATE;  -- 等待T1完成,因为id为1的行被T1锁定

-- 等待T1提交后,选中未被锁定的下一行

UPDATE orders SET status = 'completed' WHERE id = 2;

COMMIT;

在这种情况下,SELECT ... FOR UPDATE 确保了事务T1对选中行的独占访问权。只有在T1提交后,T2才能继续并对数据进行操作,从而避免了数据竞争和不一致问题。

结论

  • SELECT ... FOR UPDATE 在MySQL中为选中的行加上写锁(排他锁)。
  • 写锁确保其他事务在当前事务提交之前无法读取或修改被锁定的行。
  • 这种机制有助于在高并发环境中确保数据的一致性和正确性。

标签:status,...,事务,xxx,update,UPDATE,SELECT,id,Select
From: https://www.cnblogs.com/gongchengship/p/18284791

相关文章

  • 7.1.SQL注入-基于函数报错的方式来利用updatexml()
    基于函数报错的方式来进行利用-字符型(本页updatexml())前提条件是后台数据库没有屏蔽数据库语法报错信息updatexml()方法详解注释:第一个参数,意思就是xml文档的名称第二个参数,意思就是定位到xml文档中指定的某一个位置的更新第三个参数,意思就是将文档中指定某一个位置......
  • 8.SQL注入-基于insert,update利用案例
    SQL注入-基于insert/update利用案例sql语句正常插入表中的数据insertintomember(username,pw,sex,phonenum,address,email)values('xiaoqiang',1111,1,2,3,4);select*frommember;例如插入小强数据,如图所示:采用or这个运算符,构造闭合语句values('xiaoqiang'or......
  • 微调qwen-VL报错ValueError: Unrecognized configuration class<xxx>to build an AutoTo
    问题在微调qwen-vl的时候,微调完成之后,模型也保存好了,但是用保存的模型进行推理的时候报错,看样子是找不到分词器tokenizer。报错信息ValueError:Unrecognizedconfigurationclass<class‘transformers_modules.configuration_qwen.QWenConfig’>tobuildanAutoTok......
  • Win10关闭断电三次修复功能,并彻底关闭windowupdate服务
    应用环境:某些环境断电较频繁,断电3次造成系统修复,系统修复又需要人为干预,从而造成生产停止,因此需要禁用该功能。1、关闭断电三次修复功能以管理者权限运行cmd.exebcdedit/setbootstatuspolicyignoreallfailuresbcdedit/setrecoveryenabledNobcdedit/set{current}boot......
  • selenium07_select下拉框
    有以下2种方法定位下拉框: 方法一:最基本的元素定位方法,定位下拉框,再定位下拉框中的元素 方法二:fromselenium.webdriver.support.selectimportSelectel=driver.find_element_by_id("nr")  #先定位到下拉框Select(el).select_by_index(0)  #通过下拉框中元素的......
  • Google Earth Engine(GEE)——ui.Select的使用和分析
    结果 函数ui.Select(items, placeholder, value, onChange, disabled, style)Aprintableselectmenuwithacallback.Arguments:items(List<Object>,optional):Thelistofoptionstoaddtotheselect.Defaultstoanemptyarray.placeholder(String......
  • 前端在for循环中使用Element-plus el-select中的@click.native动态传参
    <el-tableref="table":data="editTableVariables"@cell-dblclick="handleRowDblClick"style="width:100%"><!--el-table-column:表格列组件,定义每列的展示内容和属性--><el-table-columnprop=&q......
  • MyBatis2(MyBatis基础配置 动态代理 映射器 select 元素 insert 元素 update 元素和del
    目录一、MyBatis基础配置1.MyBatis配置文件2.<configuration>元素3.<enviroments>元素4.<properties>元素5.<typeAliases>元素6.<mappers>元素二、动态代理三、映射器1.映射器与接口2. 映射器的引入 3.映射器的组成 四、select元素参数传递多......
  • Update Queries(Round 954)
    #include<bits/stdc++.h>#defineendl'\n'usingll=longlong;typedefunsignedlonglongull;usingnamespacestd;voidGordenGhost();signedmain(){#ifdefGordenfreopen("in.txt","rt",stdin);freopen......
  • es启动报错exception during geoip databases update
    解决:在es配置文件中添加如下重启ingest.geoip.downloader.enabled:false  报错信息{"type":"server","timestamp":"2024-07-01T15:11:05,965Z","level":"ERROR","component":"o.e.i.g.GeoIpDownlo......