当前位置:   article > 正文

分布式锁(二)__基于数据库实现_mybatis返回值实现分布式锁

mybatis返回值实现分布式锁

原理介绍:

要基于数据库实现分布式锁,最简单的方式可能就是直接创建一张锁表,然后通过操作该表中的数据来实现。

当需要锁住某个方法或资源时,就在该表中增加一条记录,想要释放锁的时候就删除这条记录

问题:

1.这把锁依赖数据库的可用性,数据库是一个单点,一旦数据库挂掉,会导致业务系统不可用

2.这把锁没有失效时间,一旦解锁操作失败,会导致锁记录一直在数据库中,其他线程无法再获得锁

3.这把锁只能是非阻塞的,因为数据的insert操作,一旦插入失败就会直接报错,没有获得锁的线程并不会进入排队队列,想要再次获得锁就要再次触发获得锁操作。

4,这把锁是非重入的,同一个线程没有释放锁之前无法在获得该锁,因为数据已经存在了。

 

解决:

1.数据库是单点?搞两个数据库,数据之前双向同步,一旦挂掉迅速切换到备库上。

2.没有失效时间?做一个定时任务,每隔一定时间把数据库中的超时数据清理一遍。

3.非阻塞?搞一个while循环,直到insert成功再返回

4.非重入的?在数据库中加个字段,记录当前获得锁的机器的主机信息和线程信息,那么下次再获取的时候先查询数据库,如果当前机器的主机信息和线程信息在数据库可以查询的话,直接把锁分配给他就可以了。

基于数据库排他锁:

除了可以通过增删操作数据库中的记录以外,其实还可以借助数据中自带的锁来实现分布式的锁。

在查询语句后面增加for update语句,数据库会在查询过程中给数据表增加排他锁(innodb引擎在加锁的时候,只有通过索引进行检索的时候才会使用行级锁,否则会使用表级锁)

使用数据库来实现分布式锁的方式,这两种方式都是依赖数据库的一张表,一种是通过表中的记录的存在情况确定当前是否有锁存在,另外一种是通过数据库的排他锁来实现分布式锁。

引入依赖:

  1. <dependency>
  2. <groupId>org.mybatis</groupId>
  3. <artifactId>mybatis</artifactId>
  4. <version>3.4.6</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>mysql</groupId>
  8. <artifactId>mysql-connector-java</artifactId>
  9. <version>8.0.11</version>
  10. </dependency>

创建表user:

  1. CREATE TABLE `user` (
  2. `user_id` varchar(255) NOT NULL,
  3. `user_name` varchar(255) DEFAULT NULL,
  4. PRIMARY KEY (`user_id`)
  5. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

MybatisConfig.xml:

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE configuration
  3. PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  4. "http://mybatis.org/dtd/mybatis-3-config.dtd">
  5. <configuration>
  6. <settings>
  7. <!-- 这个地方很重要 设置sql的执行时间 超过时间 没有完成 就会抛出异常 这里设置一秒 -->
  8. <setting name="defaultStatementTimeout" value="1" />
  9. </settings>
  10. <environments default="mysql">
  11. <environment id="mysql">
  12. <!-- 配置事务 -->
  13. <transactionManager type="JDBC"></transactionManager>
  14. <!-- 配置数据源 -->
  15. <dataSource type="POOLED">
  16. <property name="driver" value="com.mysql.cj.jdbc.Driver" />
  17. <property name="url" value="jdbc:mysql://localhost:3306/clouddb01?useSSL=true" />
  18. <property name="username" value="root" />
  19. <property name="password" value="123456" />
  20. <property name="poolMaximumIdleConnections" value="50" />
  21. <property name="poolMaximumActiveConnections" value="1000" />
  22. <property name="poolPingQuery" value="SELECT 1 FROM DUAL" />
  23. <property name="poolPingEnabled" value="true" />
  24. </dataSource>
  25. </environment>
  26. </environments>
  27. <mappers>
  28. <mapper resource="com/th/mapper/userMapper.xml" />
  29. </mappers>
  30. </configuration>

userMapper.xml

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  3. <!-- namespace:命名空间,是某一个 dao 层的具体路径 -->
  4. <mapper namespace="com.th.dao.user">
  5. <resultMap type="com.th.entity.User" id="User">
  6. <result property="userId" column="user_id" />
  7. <result property="userName" column="user_name" />
  8. </resultMap>
  9. <select id="getUserAllEentity" resultType="com.th.entity.User" resultMap="User">
  10. SELECT * FROM user
  11. </select>
  12. <update id="update" parameterType="com.th.entity.User">
  13. UPDATE user SET user_name = #{userName} WHERE user_id=#{userId}
  14. </update>
  15. </mapper>

user:

  1. package com.th.entity;
  2. public class User {
  3. private String userId;
  4. private String userName;
  5. public User() {
  6. super();
  7. }
  8. public User(String userId, String userName) {
  9. super();
  10. this.userId = userId;
  11. this.userName = userName;
  12. }
  13. public String getUserId() {
  14. return userId;
  15. }
  16. public void setUserId(String userId) {
  17. this.userId = userId;
  18. }
  19. public String getUserName() {
  20. return userName;
  21. }
  22. public void setUserName(String userName) {
  23. this.userName = userName;
  24. }
  25. }

和一 zk分布式锁一样的OrderService接口:

  1. public interface OrderService {
  2. void createOrder();
  3. }

模式公共资源的OrderCodeGenerator:

  1. package com.th.order;
  2. import java.text.SimpleDateFormat;
  3. import java.util.Date;
  4. public class OrderCodeGenerator {
  5. private static int i = 0;
  6. public String getOrderCode() {
  7. Date now = new Date();
  8. SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss-");
  9. return sdf.format(now) + ++i;
  10. }
  11. }

基于数据库实现的lock:

  1. package com.th.order;
  2. import java.io.IOException;
  3. import java.io.InputStream;
  4. import java.util.concurrent.TimeUnit;
  5. import java.util.concurrent.locks.Condition;
  6. import java.util.concurrent.locks.Lock;
  7. import org.apache.ibatis.io.Resources;
  8. import org.apache.ibatis.session.SqlSession;
  9. import org.apache.ibatis.session.SqlSessionFactory;
  10. import org.apache.ibatis.session.SqlSessionFactoryBuilder;
  11. import com.th.entity.User;
  12. public class DbLock implements Lock {
  13. private SqlSession session;
  14. private User user;
  15. DbLock(User user) {
  16. InputStream inputStream = null;
  17. try {
  18. inputStream = Resources.getResourceAsStream("MybatisConfig.xml");
  19. } catch (IOException e) {
  20. e.printStackTrace();
  21. }
  22. SqlSessionFactoryBuilder sessionFactoryBuilder = new SqlSessionFactoryBuilder();
  23. SqlSessionFactory sessionFactory = sessionFactoryBuilder.build(inputStream);
  24. this.session = sessionFactory.openSession();
  25. this.user = user;
  26. }
  27. @Override
  28. public void lock() {
  29. tryLock();
  30. }
  31. @Override
  32. public boolean tryLock() {
  33. int count = session.update("com.th.dao.user.update", user);
  34. return count == 1 ? true : false;
  35. }
  36. @Override
  37. public void unlock() {
  38. session.commit();
  39. }
  40. @Override
  41. public void lockInterruptibly() throws InterruptedException {
  42. // TODO Auto-generated method stub
  43. }
  44. @Override
  45. public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
  46. return false;
  47. }
  48. @Override
  49. public Condition newCondition() {
  50. return null;
  51. }
  52. }

OrderServiceImplWithZkDis 及测试方法main:

  1. package com.th.order;
  2. import java.util.concurrent.CyclicBarrier;
  3. import java.util.concurrent.locks.Lock;
  4. import com.th.entity.User;
  5. public class OrderServiceImplWithZkDis implements OrderService {
  6. private static OrderCodeGenerator org = new OrderCodeGenerator();
  7. // private Lock lock = new ZookeeperDisLock("/LOCK_TEST");
  8. // private Lock lock = new ZookeeperReAbleDisLock("/LOCK_TEST");
  9. private Lock lock = new DbLock(new User("1","张三丰"));
  10. @Override
  11. public void createOrder() {
  12. String orderCode = null;
  13. try {
  14. lock.lock();
  15. orderCode = org.getOrderCode();
  16. //TestReLock();
  17. System.out.println(Thread.currentThread().getName() + "生成订单:" + orderCode);
  18. } catch (Exception e) {
  19. e.printStackTrace();
  20. } finally {
  21. lock.unlock();
  22. }
  23. }
  24. public void TestReLock() {
  25. lock.lock();
  26. System.out.println(Thread.currentThread().getName() + "测试重入锁成功...");
  27. lock.unlock();
  28. }
  29. public static void main(String[] args) {
  30. int num = 20;
  31. CyclicBarrier cyclicBarrier = new CyclicBarrier(num);
  32. for (int i = 0; i < num; i++) {
  33. new Thread(new Runnable() {
  34. @Override
  35. public void run() {
  36. OrderService orderService = new OrderServiceImplWithZkDis();
  37. System.out.println(Thread.currentThread().getName() + ": 我准备好了");
  38. try {
  39. cyclicBarrier.await();
  40. } catch (Exception e) {
  41. e.printStackTrace();
  42. }
  43. orderService.createOrder();
  44. }
  45. }).start();
  46. }
  47. }
  48. }

测试结果:

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/煮酒与君饮/article/detail/954355
推荐阅读
相关标签
  

闽ICP备14008679号