1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > SELECT... FOR UPDATE 排他锁

SELECT... FOR UPDATE 排他锁

时间:2023-11-19 20:26:21

相关推荐

SELECT... FOR UPDATE 排他锁

SELECT… FOR UPDATE 排他锁

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="/POM/4.0.0" xmlns:xsi="/2001/XMLSchema-instance"xsi:schemaLocation="/POM/4.0.0 /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 mapperPUBLIC "-////DTD Mapper 3.0//EN""/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 CONFIGWHERE 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> {/*** 通过名称和value值,获取增加排他锁的配置* @param name* @param value*/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.ponent;@Componentpublic class ConfigDaoImpl extends ServiceImpl<ConfigMapper, Config> implements ConfigDao {private ConfigMapper configMapper;@Autowiredpublic void setConfigMapper(ConfigMapper configMapper) {this.configMapper = configMapper;}@Overridepublic 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@Setterpublic 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@SpringBootApplicationpublic 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.ponent;import org.springframework.transaction.annotation.Transactional;/*** 排他锁验证*/@Slf4j@Componentpublic class ExclusiveLocksRunner implements ApplicationRunner {private static final String RESULT_KEY = "result";private static final String RESULT = "false";private ConfigDao configDao;@Autowiredpublic void setConfigDao(ConfigDao configDao) {this.configDao = configDao;}@Transactional(rollbackFor = Exception.class)@Overridepublic 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。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。