分布式锁-Redis解决方案和Redisson解决方案
分布式锁-数据库mysql解决方案
分布式锁-Redis红锁解决方案
1:分布式锁的概念
1:概念
分布式锁(多服务共享锁) 在分布式的部署环境下,通过锁机制来让多客户端互斥的对共享资源进行访问
控制分布式系统不同进程共同访问共享资源的一种锁的实现。如果不同的系统或同一个系统的不同主机之间共享了某个临界资源,往往需要互斥来防止彼此干扰,以保证一致性。
2:锁/分布式锁/事务区别
- 锁 单进程的系统中,存在多线程同时操作一个公共变量,此时需要加锁对变量进行同步操作,保证多线程的操作线性执行消除并发修改。解决的是单进程中的多线程并发问题。
- 分布式锁 只要的应用场景是在集群模式的多个相同服务,可能会部署在不同机器上,解决进程间安全问题,防止多进程同时操作一个变量或者数据库。解决的是多进程的并发问题 事务 解决一个会话过程中,上下文的修改对所有数据库表的操作要么全部成功,要不全部失败。所以应用在service层。解决的是一个会话中的操作的数据一致性。
- 分布式事务 解决一个联动操作,比如一个商品的买卖分为添加商品到购物车、修改商品库存,此时购物车服务和商品库存服务可能部署在两台电脑,这时候需要保证对两个服务的操作都全部成功或者全部回退。解决的是组合服务的数据操作的一致性问题
2:本文使用的案例场景
1:需求
当在打车软件中,乘客下了订单。多个司机抢单,此时因为单子只有一个,多个司机对此共享资源进行抢,此处应该使用分布式锁;
2:controller层代码
@GetMapping("/do/{orderId}")
public String grab(@PathVariable("orderId") int orderId, int driverId){
System.out.println("order:"+orderId+",driverId:"+driverId);
grabService.grabOrder(orderId,driverId);
return "";
}
3:锁控制层代码(使用synchronized 不成功)
使用synchronized 不能保证多台服务器只有一个抢成功;因为synchronized 只能锁本服务的资源;多台服务的资源是锁不住的;
@Autowired
OrderService orderService;
@Override
public String grabOrder(int orderId, int driverId) {
String lock = (orderId+"");
synchronized (lock.intern()) {
try {
System.out.println("司机:"+driverId+" 执行抢单逻辑");
boolean b = orderService.grab(orderId, driverId);
if(b) {
System.out.println("司机:"+driverId+" 抢单成功");
}else {
System.out.println("司机:"+driverId+" 抢单失败");
}
} finally {
}
}
return null;
}
4:调用的订单业务代码
这一层就是写的伪代码,后续并不关注他
3:数据库mysql方案
1:原理
1:在数据库中创建一个抢占锁表,假如只有两个字段,id,value 2:当多个司机来抢单时,每一个试图抢单的进程都会忘数据库表中增加一行记录,记录id就是本订单id 3:设计抢占锁表的id主键不可重复,那么谁在数据库插入成功了,就是抢占锁成功 4:其他因为主键约束插入失败的,视为抢占锁失败 5:抢锁成功的,执行完业务后调用释放锁即删除哪行记录;
2:musql锁工具类
1:在此实现lock接口,开发自己的mysqllock 2:lock方法尝试加锁,加上了返回true,失败则递归或者循环调用锁 3:抢锁成功的,执行完业务后调用unlock释放锁;(删除哪行记录)
package com.online.taxi.order.lock;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import com.online.taxi.order.dao.TblOrderLockDao;
import com.online.taxi.order.entity.TblOrderLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import lombok.Data;
@Service
@Data
public class MysqlLock implements Lock {
@Autowired
private TblOrderLockDao mapper;
private ThreadLocal<TblOrderLock> orderLockThreadLocal ;
@Override
public void lock() {
if(tryLock()) {
System.out.println("尝试加锁");
return;
}
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
lock();
}
@Override
public boolean tryLock() {
try {
TblOrderLock tblOrderLock = orderLockThreadLocal.get();
mapper.insertSelective(tblOrderLock);
System.out.println("加锁对象:"+orderLockThreadLocal.get());
return true;
}catch (Exception e) {
return false;
}
}
@Override
public void unlock() {
mapper.deleteByPrimaryKey(orderLockThreadLocal.get().getOrderId());
System.out.println("解锁对象:"+orderLockThreadLocal.get());
orderLockThreadLocal.remove();
}
@Override
public void lockInterruptibly() throws InterruptedException {
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return false;
}
@Override
public Condition newCondition() {
return null;
}
}
3:业务使用代码
package com.online.taxi.order.service.impl;
import com.online.taxi.order.entity.TblOrderLock;
import com.online.taxi.order.lock.MysqlLock;
import com.online.taxi.order.service.GrabService;
import com.online.taxi.order.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service("grabMysqlLockService")
public class GrabMysqlLockServiceImpl implements GrabService {
@Autowired
private MysqlLock lock;
@Autowired
OrderService orderService;
ThreadLocal<TblOrderLock> orderLock = new ThreadLocal<>();
@Override
public String grabOrder(int orderId, int driverId) {
TblOrderLock ol = new TblOrderLock();
ol.setOrderId(orderId);
ol.setDriverId(driverId);
orderLock.set(ol);
lock.setOrderLockThreadLocal(orderLock);
lock.lock();
try {
System.out.println("司机:"+driverId+" 执行抢单逻辑");
boolean b = orderService.grab(orderId, driverId);
if(b) {
System.out.println("司机:"+driverId+" 抢单成功");
}else {
System.out.println("司机:"+driverId+" 抢单失败");
}
}finally {
lock.unlock();
}
return null;
}
}
4:优缺点
1:优点:开发量小,而且不依赖其他框架,当公司业务量小,并且不具备相关分布式锁框架技术储备时可使用
2:缺点:只适用于业务量小的情况下,如果并发量增大,对数据库io压力是很大的负担
|