我的编程空间,编程开发者的网络收藏夹
学习永远不晚

SELECT…FORUPDATE排他锁的实现

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

SELECT…FORUPDATE排他锁的实现

1. SELECT…FOR UPDATE 是什么?作用是什么?

select for update 即排他锁,排他锁又称为写锁,简称X锁,顾名思义,排他锁就是不能与其他锁并存,如一个事务获取了一个数据行的排他锁,其他事务就不能再获取该行的其他锁,包括共享锁和排他锁,但是获取排他锁的事务是可以对数据就行读取和修改。

作用:保证数据的一致性,为了在查询时,避免其他用户对该表进行插入,修改或删除等操作,造成表数据的不一致性。

2. MYSQL中如何查询是否存在锁信息?相关SQL

这里只讲述和排他锁有关内容。

2.1MYSQL INFORMATION_SCHEMA 数据库

INFORMATION_SCHEMA 是mysql自带的元数据数据库INNODB_TRX是MYSQL中事务和锁相关的表INNODB_TRX表提供了当前INNODB引擎内每个事务的信息(除只读事务外),包括当一个事务启动,事务是否在等待一个锁,以及正在执行的SQL语句。
SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX;

3. SELECT…FOR UPDATE 怎么使用?如何验证?

这里使用mysql数据库为例。

3.1 Mysql Config表SQL

-- ----------------------------
-- Table structure for config
-- ----------------------------
DROP TABLE IF EXISTS `config`;
CREATE TABLE `config`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
  `value` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE INDEX `name-index`(`name`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of config
-- ----------------------------
INSERT INTO `config` VALUES (1, 'result', 'false');

3.2 创建SpringBoot工程,结构目录如下

├─main
│  ├─java
│  │  └─com
│  │      └─xy
│  │          └─springboot
│  │              │  Application.java
│  │              ├─db
│  │              │  ├─dao
│  │              │  │  │  ConfigDao.java
│  │              │  │  ├─impl
│  │              │  │  │      ConfigDaoImpl.java
│  │              │  │  └─mapper
│  │              │  │          ConfigMapper.java
│  │              │  └─entity
│  │              │          Config.java
│  │              └─runner
│  │                      ExclusiveLocksRunner.java
│  └─resources
│      │  application.properties
│      └─Mapper
└─              ConfigMapper.xml

3.2 pom.xml文件内容

引入mysql driver、lombok、mybatis-plus等依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.5</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.xy</groupId>
    <artifactId>spring-boot</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spring-boot</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>11</java.version>
    </properties>
    <dependencies>
		<!-- 使用mybatis-plus 持久层框架 -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.1</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <!-- mysql 连接驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

3.4 db层接口及其实现类

Mapper映射xml文件内容如下:ConfigMapper.xml

<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xy.springboot.db.dao.mapper.ConfigMapper">
    <select id="getLockedConfig" resultType="com.xy.springboot.db.entity.Config">
        SELECT
        *
        FROM CONFIG
        WHERE name = #{name} AND value = ${value}
        FOR UPDATE SKIP LOCKED
    </select>
</mapper>

ConfigMapper.java 接口类

package com.xy.springboot.db.dao.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.xy.springboot.db.entity.Config;
import org.apache.ibatis.annotations.Param;

public interface ConfigMapper extends BaseMapper<Config> {
    Config getLockedConfig(@Param("name") String name, @Param("value") String value);
}

ConfigDao.java 接口类及其实现类

package com.xy.springboot.db.dao;

import com.baomidou.mybatisplus.extension.service.IService;
import com.xy.springboot.db.entity.Config;


public interface ConfigDao extends IService<Config> {

    
    Config getLockedConfig(String name, String value);
}

package com.xy.springboot.db.dao.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xy.springboot.db.dao.ConfigDao;
import com.xy.springboot.db.dao.mapper.ConfigMapper;
import com.xy.springboot.db.entity.Config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class ConfigDaoImpl extends ServiceImpl<ConfigMapper, Config> implements ConfigDao {
    private ConfigMapper configMapper;

    @Autowired
    public void setConfigMapper(ConfigMapper configMapper) {
        this.configMapper = configMapper;
    }

    @Override
    public Config getLockedConfig(String name, String value) {
        return configMapper.getLockedConfig(name, value);
    }
}

Config.java 实体类

package com.xy.springboot.db.entity;

import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Getter;
import lombok.Setter;


@TableName("config")
@Getter
@Setter
public class Config {

    private int id;

    private String name;

    private String value;
}

3.5 Application.java 开启Mapper扫描和开启事务

package com.xy.springboot;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@MapperScan(basePackages = "com.xy.springboot.db.dao.mapper")
@EnableTransactionManagement
@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

3.6 [核心] ExclusiveLocksRunner 排他锁Runner

ExclusiveLocksRunner 该类实现了ApplicationRunner接口,其含义是在SpringBoot工程启动之后,执行的操作。该类必须注入到IOC容器中。
package com.xy.springboot.runner;

import com.xy.springboot.db.dao.ConfigDao;
import com.xy.springboot.db.entity.Config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;


@Slf4j
@Component
public class ExclusiveLocksRunner implements ApplicationRunner {

    private static final String RESULT_KEY = "result";
    private static final String RESULT = "false";

    private ConfigDao configDao;

    @Autowired
    public void setConfigDao(ConfigDao configDao) {
        this.configDao = configDao;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void run(ApplicationArguments args) throws Exception {
        Config lockedConfig = configDao.getLockedConfig(RESULT_KEY, RESULT);
        if (lockedConfig == null) {
            log.error("config is null。because config is locked.");
            return;
        }
        log.info("start doing");
        lockedConfig.setValue("true");
        configDao.updateById(lockedConfig);
    }
}

3.7 疑问&并验证

疑问:

  • ExclusiveLocksRunner类中run方法上的@Transactional 注解是否起作用?
  • select…for update 是否有效?
  • select…for update 加锁之后,未释放之前,再次加锁时,返回的lockedConfig内容是什么?

断点位置:

  • 事务拦截器(使用AOP的方式对@Transactional注解进行处理)inovke方法处断点: org.springframework.transaction.interceptor.TransactionInterceptor#invoke
  • ExclusiveLocksRunner.java run 方法

Debug如图所示,证明ExclusiveLocksRunner.java中run 方法上@Transcational 注解是有效的。

进入invokeWithinTransaction 调用事务方法继续调试,

整个调用流程如下:创建事务——> 调用run方法 -> commit提交事务。

进入run方法:根据检索条件查询内容,并对其内容设置类排他锁。

LOG信息如下:

查询mysql中是否存在对应的排他锁信息

-- 执行如下信息,查看是否存在Lock信息
SELECT trx_id, trx_state, trx_started, trx_rows_locked  FROM INFORMATION_SCHEMA.INNODB_TRX;

结果如下:

证明:数据库中已经存在排他锁信息,证明该加锁方式是OK的,并在大概在第2行。

在数据库中,再次执行以下SQL,进行查询,得到结果为null。证书排他锁未释放之前,再次枷锁时,返回内容为null,因此select…for update 加锁之后,未释放之前,再次加锁时,返回的lockedConfig内容时null。

 到此这篇关于SELECT… FOR UPDATE 排他锁的实现的文章就介绍到这了,更多相关SELECT… FOR UPDATE 排他锁内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

SELECT…FORUPDATE排他锁的实现

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

SELECT…FORUPDATE排他锁的实现

本文主要介绍了SELECT…FORUPDATE排他锁的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
2023-01-12

SELECT… FOR UPDATE 排他锁的实现

目录1. SELECT…FOR UPDATE 是什么?作用是什么?2. mysql中如何查询是否存在锁信息?相关SQL2.1MYSQL INFORMATION_SCHEMA 数据库3. SELECT…FOR UP
2023-01-12

利用Java怎么实现一个排他锁

利用Java怎么实现一个排他锁?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。一 .前言某年某月某天,同事说需要一个文件排他锁功能,需求如下:(1)写操作是排他属
2023-05-31

MySQL8.0锁等待排查的实现

目录前言1. data_locks2. data_lock_waits3. sys.innodb_lock_waits4. 状态变量5. 状态变量 bug总结前言mysql 5.7 版本的时候锁等待排查用的元数据,主要存储在 inform
MySQL8.0锁等待排查的实现
2024-09-09

一文搞懂Mysql中的共享锁、排他锁、悲观锁、乐观锁及使用场景

目录一、常见锁类型二、mysql引擎介绍三、常用引擎间的区别 四、共享锁与排他锁五、排他锁的实际应用六、共享锁的实际应用七、死锁的发生八、另一种发生死锁的情景九、死锁的解决方式十、意向锁和计划锁十一、乐观锁和悲观锁总结一、常见锁类型表级锁
2022-07-04

mysql中select语句降序排列怎么实现

在MySQL中,可以使用ORDER BY子句来对查询结果进行降序排列。例如,假设我们有一个名为students的表,其中有name和score两个列,我们想要按照分数降序排列查询结果,可以使用以下语句:SELECT * FROM stud
mysql中select语句降序排列怎么实现
2024-04-09

基于redis乐观锁实现并发排队

有个需求场景是这样的,使用Redis控制scrapy运行的数量。当系统的后台设置为4时,只允许scapry启动4个任务,多余的任务则进行排队。概况最近做了一个django + scrapy + celery + redis 的爬虫系统,
2022-12-25

golang 中合并排序的递归/并行实现中出现死锁

php小编西瓜发现,在golang中使用递归或并行实现合并排序时,有可能出现死锁的问题。合并排序是一种常用的排序算法,可以有效地将一个大数组分解成多个小数组进行排序,然后再合并起来。然而,在golang的并发编程中,如果不注意控制gorou
golang 中合并排序的递归/并行实现中出现死锁
2024-02-10

Redisson 加锁解锁的实现

目录分布式锁使用getLocktryLockunLock总结分布式锁使用对于 Redisson 分布式锁的使用很简单:1编程客栈、调用 getLock 函数获取锁操作对象;2、调用 tryLock 函数进行加锁;3、调用 unlock
2022-08-18

基于redis乐观锁怎么实现并发排队

这篇“基于redis乐观锁怎么实现并发排队”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“基于redis乐观锁怎么实现并发排队
2023-07-04

Golang实现带优先级的select

这篇文章主要为大家详细介绍了如何在Golang中实现带优先级的select,文中的示例代码讲解详细,对我们学习Golang有一定的帮助,需要的可以参考一下
2023-05-14

golang select的实现原理是什么

在Go语言中,`select`语句用于从多个通道中接收数据,并且只有当其中一个通道可以接收数据时,`select`语句才会执行相应的代码块。`select`语句的实现原理是通过轮询的方式来监听通道的状态。当`select`语句执行时,它会
2023-10-27

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录