一、什么是分布式锁, 什么是分布式事务?
1.1、 分布式锁: 多进程的并发问题(传统的单体系统中我们可以用Synchronized、Lock等方式保证数据的安全,多进程中这个无效)
1.2、 分布式事务:多进程的事务管理,解决的是跨进程会话数据的一致性问题(传统我们可以利用事务管理,复合操作要么全部成功要么全部失效,分布式中事务无法直接跨进程进行回滚)
二、分布式锁和分布式事务的应用场景
2.1、分布式锁:秒杀活动,我们如何防止超卖现象。
2.2、 分布式事务: 跨进程操作中我们要防止会话中某一个或者几个操作失败,确保不产生脏数据。
三、分布式锁与分布式事务的解决方案
3.1、分布式锁
3.1.1、数据库(以MySQL举例)
获取锁:因为对表中字段name做了唯一性约束,这里如果有多个请求同时提交到数据库的话,数据库会保证只有一个操作可以成功。
释放锁:删除/修改当前的数据
CREATE TABLE `lock` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
`name` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL
DEFAULT '' COMMENT '锁定的方法名',
`desc` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL
DEFAULT '备注信息' COMMENT '备注信息',
`update_time` timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE
CURRENT_TIMESTAMP(0) COMMENT '保存数据时间,自动生成',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `uidx_name`(`name`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
3.1.2、Redis
Redis主要是利用Redis的单线程进行特性实现分布式锁,
/*
* key :产品Id
*
* value:一个唯一的时间戳
/
//获取锁
public boolean requireLock(String key, String value) {
if (redisTemplate.opsForValue().setIfAbsent(key, value)) {
return true;
}
Object current_value = redisTemplate.opsForValue().get(key);
if (!StringUtils.ObjectToString(current_value).equals("") &&
Long.parseLong(current_value.toString()) < System.currentTimeMillis()) {
String old_value =
redisTemplate.opsForValue().getAndSet(key,value).toString();
if (!StringUtils.ObjectToString(old_value).equals("") &&
old_value.equals(current_value)) {
return true;
}
}
return false;
}
//线程解锁,删除key
public void unlock(String key, String value) {
if(StringUtils.ObjectToString(redisTemplate.opsForValue().get(key)).equals(value)) {
redisTemplate.opsForValue().getOperations().delete(key);
}
}
private RedisTemplate redisTemplate;
public RedisLock(RedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
3.1.3、Zookeeper
实现分布式锁的原理: 1.1、利用zookeeper树形结点的特性, 一个结点只能下面唯一的临时结点,唯一性。 1.2、唯一结点的有序性,顺序为 从小到大。 1.3、watch监控机制,当结点删除的时候会发送了一个通知
//Zookeepr实现分布式锁
public class CustomizedZKLock extends ReentrantLock {
private ZookeeperUtils zkUtils;
private String lockNodeName;
private String lockPath = "/lock";
private String lockStr = "lock0";
private CountDownLatch countDownLatch;
public CustomizedZKLock(String zkConnectStr, String lockStr) {
if (lockStr != null) {
this.lockStr = lockStr;
}
try {
zkUtils = new ZookeeperUtils(zkConnectStr);
} catch (IOException e) {
e.printStackTrace();
}
}
public CustomizedZKLock(String zkConnectStr) {
this(zkConnectStr, null);
}
//所有的线程在进行获取锁
@Override
public void lock() {
super.lock();
countDownLatch = new CountDownLatch(1);
//在zk中创建临时有序节点
lockNodeName = zkUtils.createZNodeES(lockPath, "/" + lockStr, this::tryGetLock);
//阻塞等待获取资源
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//当zk节点变动时尝试获取锁
private void tryGetLock(WatchedEvent watchedEvent) {
if (tryGetLock()) {
countDownLatch.countDown();
}
}
private boolean tryGetLock() {
List list = zkUtils.getChildNode(lockPath);
list.sort(String::compareTo);
list = list.stream().
filter(s -> s.startsWith(lockStr)).collect(Collectors.toList());
//如果获取到锁
if (lockNodeName.equals(lockPath + "/" + list.get(0))) {
return true;
}
return false;
}
@Override
public void unlock() {
//释放锁并删除zk节点
zkUtils.deleteNode(lockNodeName);
zkUtils.close();
super.unlock();
}
}
//系统工具类
public class ZookeeperUtils {
private ZooKeeper zookeeper;
public ZookeeperUtils(String zkConnectStr) throws IOException
{
zookeeper = new ZooKeeper(zkConnectStr, 2000, null);
}
/**
* 创建临时有序节点
*/
public String createZNodeES(String path, String node, Watcher watch) {
try {
watch(path, watch);
return zookeeper.create(path + node,new byte[1], ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL_SEQUENTIAL);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 对某个节点添加监控
* @param path
* @param watcher
*/
private void watch(String path,Watcher watcher) {
try {
zookeeper.getChildren(path, e -> {
watcher.process(e);
watch(path,watcher);
});
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 关闭连接
*/
public void close() {
try {
zookeeper.close();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 获取子节点列表
* @return
*/
public List getChildNode(String parentPath) {
try {
return zookeeper.getChildren(parentPath, true);
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
/**
* 删除节点
* @param znodePath 要删除的节点路径
*/
public void deleteNode(String znodePath) {
try {
zookeeper.delete(znodePath,-1);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (KeeperException e) {
e.printStackTrace();
}
}
}
//系统测试类
public class Main {
public static void main(String[] args) throws InterruptedException {
CustomizedZKLock zkLock = new CustomizedZKLock("172.16.104.1:2181");
zkLock.lock();
System.out.println("获得锁");
Thread.sleep(10000);
zkLock.unlock();
System.out.println("释放锁");
}
}
3.2、分布式事务
3.2.1、2PC理论以及消息通知机制