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等依赖

  4.0.0 org.springframework.bootspring-boot-starter-parent2.6.5com.xyspring-boot0.0.1-SNAPSHOTspring-bootDemo project for Spring Boot 11  com.baomidoumybatis-plus-boot-starter3.5.1 org.springframework.bootspring-boot-devtoolsruntimetrue mysqlmysql-connector-javaruntime org.projectlomboklomboktrue org.springframework.bootspring-boot-starter-testtest   org.springframework.bootspring-boot-maven-plugin   org.projectlomboklombok

3.4 db层接口及其实现类

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

 

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 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 { /** * 通过名称和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.springframework.stereotype.Component; @Component public class ConfigDaoImpl extends ServiceImpl 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 排他锁内容请搜索0133技术站以前的文章或继续浏览下面的相关文章希望大家以后多多支持0133技术站!

以上就是SELECT… FOR UPDATE 排他锁的实现的详细内容,更多请关注0133技术站其它相关文章!

赞(0) 打赏
未经允许不得转载:0133技术站首页 » 数据库