您的位置:首页 > 数据库 > Redis

redis锁实现分布式取号思路

2017-12-15 10:49 232 查看
最近看到分布式锁这块,根据其他大神的说法,总结了几种基本实现方式

1、数据库乐观锁

2、redis锁

3、zookeeper

闲来无事,就写了一个相对实现比较简单的redis锁。不善于描述,直接贴代码吧!

1、maven带入相关jar包

<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>


2、获取锁和释放锁的相关代码

public class RedisLock {

private static JedisPool jedisPool;

static {
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(100);
config.setMaxIdle(100);
config.setMaxWaitMillis(3000);
config.setTestOnBorrow(false);
config.setMinIdle(10);
jedisPool = new JedisPool(config, "127.0.0.1", 6379);
}

private static Jedis getClient() {
return jedisPool.getResource();
}

private static String key = "redis.lock";
private static String val = "true";
private static int expire = 60; //过期时间

//获取锁
public static boolean getLock() {
for (; ; ) {
Jedis jedis = null;
try {
jedis = getClient();
Long result = jedis.setnx(key, val);
if (result == 1) {
jedis.expire(key, expire);
return true;
}
System.out.println(Thread.currentThread().getName() + "正在自旋");
try {
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
e.printStackTrace();
}
} finally {
jedisPool.returnResource(jedis);
}
}
}

//释放锁
public static void relaseLock() {
getClient().expire(key, -1);
System.out.println("释放锁成功");
}
}


3、一个简单的数据记录中心,提供批量获取顺序号的方法

public class DataRange {

private Long start;

private Long end;

public Long getStart() {
return start;
}

public void setStart(Long start) {
this.start = start;
}

public void setEnd(Long end) {
this.end = end;
}

public Long getEnd() {
return end;
}

@Override
public String toString() {
return "DataRange{" +
"start=" + start +
", end=" + end +
'}';
}
}


public class DataCenter {

private static Long num = 0L;

private static Integer step = 500;

public static DataRange getRangeNos()
ced6
throws Exception {
if (RedisLock.getLock()) {
DataRange range = new DataRange();
range.setStart(num);
num += step;
range.setEnd(num - 1);

//            TimeUnit.SECONDS.sleep(10);
RedisLock.relaseLock();
return range;
}
throw new Exception("获取流水号失败");
}
}


4、一个补齐流水号和日期生成完整流水号的辅助类

public class DataUtil {

private static final int length = 8;

private static final String addc = "0";

private static SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd");

public static String complete(Long l) throws Exception {
String dateStr = format.format(new Date());
String ls = l.toString();
int lsl = ls.length();
if (lsl < length) {
StringBuilder sb = new StringBuilder(dateStr);
for (int i = 0; i < length - lsl; i++) {
sb.append(addc);
}
return sb.append(ls).toString();
} else if (lsl == length) {
return dateStr + ls;
} else {
throw new Exception("out of range");
}
}
}


5、创建调用客户端,此处以线程表示

public class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
DataRange range = DataCenter.getRangeNos();
System.out.println(this.getName() + "获取数组序列号" + range);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}


6、创建Test类

public class Test {

public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
Thread thread = new MyThread();
thread.start();
}
}
}


输出结果:

Thread-6正在自旋

Thread-2正在自旋

Thread-5正在自旋

Thread-0正在自旋

Thread-4正在自旋

Thread-8正在自旋

Thread-7正在自旋

释放锁成功

Thread-3获取数组序列号DataRange{start=0, end=499}

释放锁成功

Thread-3获取数组序列号DataRange{start=500, end=999}

释放锁成功

Thread-3获取数组序列号DataRange{start=1000, end=1499}

释放锁成功

Thread-3获取数组序列号DataRange{start=1500, end=1999}

释放锁成功

Thread-3获取数组序列号DataRange{start=2000, end=2499}

Thread-3正在自旋

释放锁成功

Thread-1获取数组序列号DataRange{start=2500, end=2999}

释放锁成功

Thread-1获取数组序列号DataRange{start=3000, end=3499}

释放锁成功

Thread-1获取数组序列号DataRange{start=3500, end=3999}

Thread-1正在自旋

释放锁成功

Thread-9获取数组序列号DataRange{start=4000, end=4499}

释放锁成功

Thread-9获取数组序列号DataRange{start=4500, end=4999}

释放锁成功

Thread-9获取数组序列号DataRange{start=5000, end=5499}

释放锁成功

Thread-9获取数组序列号DataRange{start=5500, end=5999}

释放锁成功

Thread-9获取数组序列号DataRange{start=6000, end=6499}

释放锁成功

Thread-9获取数组序列号DataRange{start=6500, end=6999}

释放锁成功

Thread-9获取数组序列号DataRange{start=7000, end=7499}

释放锁成功

Thread-9获取数组序列号DataRange{start=7500, end=7999}

释放锁成功

Thread-9获取数组序列号DataRange{start=8000, end=8499}

释放锁成功

Thread-9获取数组序列号DataRange{start=8500, end=8999}

Thread-2正在自旋

Thread-5正在自旋

释放锁成功

Thread-6获取数组序列号DataRange{start=9000, end=9499}

释放锁成功

Thread-6获取数组序列号DataRange{start=9500, end=9999}

释放锁成功

Thread-6获取数组序列号DataRange{start=10000, end=10499}

释放锁成功

Thread-6获取数组序列号DataRange{start=10500, end=10999}

Thread-4正在自旋

Thread-8正在自旋

Thread-6正在自旋

释放锁成功

Thread-0获取数组序列号DataRange{start=11000, end=11499}

释放锁成功

Thread-0获取数组序列号DataRange{start=11500, end=11999}

Thread-0正在自旋

释放锁成功

Thread-7获取数组序列号DataRange{start=12000, end=12499}

释放锁成功

Thread-7获取数组序列号DataRange{start=12500, end=12999}

释放锁成功

Thread-7获取数组序列号DataRange{start=13000, end=13499}

释放锁成功

Thread-7获取数组序列号DataRange{start=13500, end=13999}

释放锁成功

Thread-7获取数组序列号DataRange{start=14000, end=14499}

Thread-7正在自旋

释放锁成功

Thread-3获取数组序列号DataRange{start=14500, end=14999}

释放锁成功

Thread-3获取数组序列号DataRange{start=15000, end=15499}

释放锁成功

Thread-3获取数组序列号DataRange{start=15500, end=15999}

释放锁成功

Thread-3获取数组序列号DataRange{start=16000, end=16499}

释放锁成功

Thread-3获取数组序列号DataRange{start=16500, end=16999}

释放锁成功

Thread-1获取数组序列号DataRange{start=17000, end=17499}

释放锁成功

Thread-1获取数组序列号DataRange{start=17500, end=17999}

释放锁成功

Thread-1获取数组序列号DataRange{start=18000, end=18499}

释放锁成功

Thread-1获取数组序列号DataRange{start=18500, end=18999}

释放锁成功

Thread-1获取数组序列号DataRange{start=19000, end=19499}

释放锁成功

Thread-1获取数组序列号DataRange{start=19500, end=19999}

释放锁成功

Thread-1获取数组序列号DataRange{start=20000, end=20499}

释放锁成功

Thread-2获取数组序列号DataRange{start=20500, end=20999}

Thread-2正在自旋

释放锁成功

Thread-5获取数组序列号DataRange{start=21000, end=21499}

释放锁成功

Thread-5获取数组序列号DataRange{start=21500, end=21999}

释放锁成功

Thread-5获取数组序列号DataRange{start=22000, end=22499}

释放锁成功

Thread-5获取数组序列号DataRange{start=22500, end=22999}

释放锁成功

Thread-5获取数组序列号DataRange{start=23000, end=23499}

释放锁成功

Thread-5获取数组序列号DataRange{start=23500, end=23999}

Thread-8正在自旋

Thread-4正在自旋

释放锁成功

Thread-6获取数组序列号DataRange{start=24500, end=24999}

释放锁成功

Thread-5获取数组序列号DataRange{start=24000, end=24499}

Thread-5正在自旋

释放锁成功

Thread-6获取数组序列号DataRange{start=25000, end=25499}

释放锁成功

Thread-6获取数组序列号DataRange{start=25500, end=25999}

释放锁成功

Thread-6获取数组序列号DataRange{start=26000, end=26499}

释放锁成功

Thread-6获取数组序列号DataRange{start=26500, end=26999}

释放锁成功

Thread-6获取数组序列号DataRange{start=27000, end=27499}

释放锁成功

Thread-0获取数组序列号DataRange{start=27500, end=27999}

释放锁成功

Thread-0获取数组序列号DataRange{start=28000, end=28499}

释放锁成功

Thread-0获取数组序列号DataRange{start=28500, end=28999}

释放锁成功

Thread-0获取数组序列号DataRange{start=29000, end=29499}

释放锁成功

Thread-0获取数组序列号DataRange{start=29500, end=29999}

释放锁成功

Thread-0获取数组序列号DataRange{start=30000, end=30499}

释放锁成功

Thread-0获取数组序列号DataRange{start=30500, end=30999}

释放锁成功

Thread-0获取数组序列号DataRange{start=31000, end=31499}

释放锁成功

Thread-7获取数组序列号DataRange{start=31500, end=31999}

释放锁成功

Thread-7获取数组序列号DataRange{start=32000, end=32499}

释放锁成功

Thread-7获取数组序列号DataRange{start=32500, end=32999}

释放锁成功

Thread-7获取数组序列号DataRange{start=33000, end=33499}

释放锁成功

Thread-7获取数组序列号DataRange{start=33500, end=33999}

释放锁成功

Thread-2获取数组序列号DataRange{start=34000, end=34499}

释放锁成功

Thread-2获取数组序列号DataRange{start=34500, end=34999}

释放锁成功

Thread-2获取数组序列号DataRange{start=35000, end=35499}

释放锁成功

Thread-2获取数组序列号DataRange{start=35500, end=35999}

Thread-8正在自旋

释放锁成功

Thread-2获取数组序列号DataRange{start=36000, end=36499}

Thread-5正在自旋

Thread-2正在自旋

释放锁成功

Thread-4获取数组序列号DataRange{start=36500, end=36999}

释放锁成功

Thread-4获取数组序列号DataRange{start=37000, end=37499}

释放锁成功

Thread-4获取数组序列号DataRange{start=37500, end=37999}

释放锁成功

Thread-4获取数组序列号DataRange{start=38000, end=38499}

释放锁成功

Thread-4获取数组序列号DataRange{start=38500, end=38999}

释放锁成功

Thread-4获取数组序列号DataRange{start=39000, end=39499}

释放锁成功

Thread-4获取数组序列号DataRange{start=39500, end=39999}

释放锁成功

Thread-4获取数组序列号DataRange{start=40000, end=40499}

释放锁成功

Thread-4获取数组序列号DataRange{start=40500, end=40999}

释放锁成功

Thread-4获取数组序列号DataRange{start=41000, end=41499}

释放锁成功

Thread-8获取数组序列号DataRange{start=41500, end=41999}

释放锁成功

Thread-8获取数组序列号DataRange{start=42000, end=42499}

释放锁成功

Thread-8获取数组序列号DataRange{start=42500, end=42999}

释放锁成功

Thread-8获取数组序列号DataRange{start=43000, end=43499}

Thread-8正在自旋

释放锁成功

Thread-5获取数组序列号DataRange{start=43500, end=43999}

Thread-2正在自旋

释放锁成功

Thread-5获取数组序列号DataRange{start=44000, end=44499}

释放锁成功

Thread-5获取数组序列号DataRange{start=44500, end=44999}

Thread-2正在自旋

释放锁成功

Thread-8获取数组序列号DataRange{start=45000, end=45499}

释放锁成功

Thread-8获取数组序列号DataRange{start=45500, end=45999}

释放锁成功

Thread-8获取数组序列号DataRange{start=46000, end=46499}

释放锁成功

Thread-8获取数组序列号DataRange{start=46500, end=46999}

释放锁成功

Thread-8获取数组序列号DataRange{start=47000, end=47499}

释放锁成功

Thread-8获取数组序列号DataRange{start=47500, end=47999}

释放锁成功

Thread-2获取数组序列号DataRange{start=48000, end=48499}

释放锁成功

Thread-2获取数组序列号DataRange{start=48500, end=48999}

释放锁成功

Thread-2获取数组序列号DataRange{start=49000, end=49499}

释放锁成功

Thread-2获取数组序列号DataRange{start=49500, end=49999}

总结:使用redis的setnx来控制获取哪个客户端获取锁(目前上述获取锁方法存在同步的问题,设置keyvalue和设置过期时间不同步会导致某些情况死锁),,获取的流水号每次获取多个,可供一段时间使用。由于本机测试,仅供学习参考,需要的可以自己修改完善代码。

此处datacenter可以改为数据库或者redis缓存存储

或者直接使用数据库乐观锁实现方式也可以。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  分布式锁 redis