前言
目前几乎很多大型网站及应用都是分布式部署的,分布式场景中的数据一致性问题一直是一个比较重要的话题。分布式的CAP理论告诉我们“任何一个分布式系统都无法同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance),最多只能同时满足两项。”所以,很多系统在设计之初就要对这三者做出取舍。在互联网领域的绝大多数的场景中,都需要牺牲强一致性来换取系统的高可用性,系统往往只需要保证“最终一致性”,只要这个最终时间是在用户可以接受的范围内即可。
在很多场景中,我们为了保证数据的最终一致性,需要很多的技术方案来支持,比如分布式事务、分布式锁等。有的时候,我们需要保证一个方法在同一时间内只能被同一个线程执行。
分布式锁有三种实现方式:
1.基于数据库实现分布式锁;
2.基于缓存(Redis等)实现分布式锁;
3.基于Zookeeper实现分布式锁;
本篇只是基于单JVM做测试。
一、基于数据库实现分布式锁
- 建表语句
其中lock_key为锁的名称,并设置唯一索引。
CREATE TABLE `distribute_lock` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`lock_key` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `lock_key` (`lock_key`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
- 实体类
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.io.Serializable;
@TableName("distribute_lock")
@Data
public class DistributeLock implements Serializable {
@TableId(value = "id", type = IdType.AUTO)
private Long id;
@TableField("lock_key")
private String lockKey;
}
3. 测试
此处数据库相关操作使用Mybatis-Plus实现。
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.entity.DistributeLock;
import com.service.LockService;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
public class MySQLDistributedLock {
@Autowired
private LockService lockService;
//锁名称
private static String LOCK_KEY = "lock_key";
@Test
public void testLock() throws InterruptedException {
new Thread(new Runnable() {
@Override
public void run() {
boolean flag = false;
try {
DistributeLock lock = new DistributeLock();
lock.setLockKey(LOCK_KEY);
flag = lockService.save(lock);
if (flag) {
log.info(Thread.currentThread().getName() + ":线程1获取锁成功");
//模拟业务场景
Thread.sleep(3000);
}
} catch (Exception e) {
log.error(Thread.currentThread().getName() + ":线程1获取锁异常");
} finally {
//只有抢锁成功才能释放锁,防止将其他线程持有的锁释放
if (flag) {
lockService.remove(Wrappers.<DistributeLock>lambdaQuery().eq(DistributeLock::getLockKey, LOCK_KEY));
log