1 分布式锁介绍
1.1 什么是分布式
一个大型的系统往往被分为几个子系统来做,一个子系统可以部署在一台机器的多个 JVM(java虚拟机) 上,也可以部署在多台机器上。但是每一个系统不是独立的,不是完全独立的。需要相互通信,共同实现业务功能。
一句话来说:分布式就是通过计算机网络将后端工作分布到多台主机上,多个主机一起协同完成工作。
1.2 什么是锁--作用安全
现实生活中,当我们需要保护一样东西的时候,就会使用锁。例如门锁,车锁等等。很多时候可能许多人会共用这些资源,就会有很多个钥匙。但是有些时候我们希望使用的时候是独自不受打扰的,那么就会在使用的时候从里面反锁,等使用完了再从里面解锁。这样其他人就可以继续使用了。
JAVA程序中,当存在多个线程可以同时改变某个变量(可变共享变量)时,就需要对变量或代码块做同步,使其在修改这种变量时能够线性执行消除并发修改变量,而同步的本质是通过锁来实现的。如 Java 中 synchronize 是在对象头设置标记,Lock 接口的实现类基本上都只是某一个 volitile 修饰的 int 型变量其保证每个线程都能拥有对该 int 的可见性和原子修改
1.3 什么是分布式锁
任何一个分布式系统都无法同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance),最多只能同时满足两项。CAP
当在分布式模型下,数据只有一份(或有限制),此时需要利用锁的技术控制某一时刻修改数据的进程数。
分布式锁: 在分布式环境下,多个程序/线程都需要对某一份(或有限制)的数据进行修改时,针对程序进行控制,保证同一时间节点下,只有一个程序/线程对数据进行操作的技术。
1.4 分布式锁具备的条件
互斥性:同一时刻只能有一个服务(或应用)访问资源,特殊情况下有读写锁
原子性:一致性要求保证加锁和解锁的行为是原子性的
安全性:锁只能被持有该锁的服务(或应用)释放,避免死锁
容错性:在持有锁的服务崩溃时,锁仍能得到释放避免死锁
可重用性:同一个客户端获得锁后可递归调用---重入锁和不可重入锁
公平性:看业务是否需要公平,避免饿死--公平锁和非公平锁
支持阻塞和非阻塞:和 ReentrantLock 一样支持 lock 和 trylock 以及 tryLock(long timeOut)---阻塞锁和非阻塞锁==PS:::自旋锁==
高可用:获取锁和释放锁 要高可用
高性能:获取锁和释放锁的性能要好
持久性:锁按业务需要自动续约/自动延期
2.分布式锁的解决方案
2.1 数据库实现分布式锁
2.1.1 基于数据库表实现
准备工作:创建tb_program表,用于记录当前哪个程序正在使用数据
CREATE TABLE `tb_program` (
`program_no` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '程序的编号'
PRIMARY KEY (`program_no`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
实现步骤:
程序访问数据时,将程序的编号(insert)存入tb_program表;
当insert成功,代表该程序获得了锁,即可执行逻辑;
当program_no相同的其他程序进行insert是,由于主键冲突会导致insert失败,则代表获取锁失败;
获取锁成功的程序在逻辑执行完以后,删除该数据,代表释放锁。
2.1.2 基于数据库的排它锁实现
除了可以通过增删操作数据表中的记录以外,其实还可以借助数据中自带的锁来实现分布式的锁。我们还用刚刚创建的那张数据库表,基于MySql的InnoDB引擎(MYSQL的引擎种类)可以通过数据库的排他锁来实现分布式锁。
实现步骤:
在查询语句后面增加for update,数据库会在查询过程中给数据库表增加排他锁。当某条记录被加上排他锁之后,其他线程无法再在该行记录上增加排他锁
获得排它锁的线程即可获得分布式锁,执行方法的业务逻辑
执行完方法之后,再通过connection.commit();操作来释放锁。
实现代码
pom.xml
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.itheima</groupId>
<artifactId>mysql-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<!--依赖包-->
<dependencies>
<!-- MySql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.32</version>
</dependency>
<!-- Test dependencies -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Book
public class Book {
// 图书ID
private Integer id;
// 图书名称
private String name;
// 图书价格
private Float price;
// 图书图片
private String pic;
// 图书描述
private String desc;
}
BookDao
public interface BookDao {
/**
* 查询所有的book数据
* @return
*/
List<