CAP理论: Consistency + Availability + Partition Fault
这3者不可能都达到,一般会牺牲强一致性,追求最终一致性。
分布式锁需要解决什么问题?
1)如何保证一个方法只有一个机器上的一个线程访问
2)如何并且高性能地获得锁,释放锁
3)阻塞锁
4)可重入锁
1.使用数据库实现
1)通过insert数据获得锁,delete数据释放锁
insert into lock(method_name) values(‘method_name‘)
delete from lock where method_name = ‘method_name‘
优点: 简单易理解实现
缺点: a)数据库有单点故障(用多个数据库)
b)如果释放锁失败,别的客户端永远获得不到锁(用schedule job定期清理数据)
c)如果获得锁失败,会直接返回,不会进入队列等待直到重新获得锁(用while true阻塞)
d)实现不了可重入锁(记住获得锁的主机信息和线程信息)
2)通过数据库自带的排它锁
public boolean tryLock(){
connection.setAutoCommit(false);
while(true){
try{
result = select * from lock where method_name = ‘method_name‘ for update
if(result == null) return true;
}Catch(Exception e){
}
sleep(1000);
}
return false;
}
public boolean unLock(){
connection.commit();
}
能解决上面的 b), c) 问题, 但这个锁必须是行级锁,需要创建唯一索引,当表很小时,就算创建的是唯一索引也会被优化成表级锁。
2.使用缓存实现(Redis, Memcached)
获得锁: map.put(key, value)
释放锁: map.get(key)
优点: 能解决单点故障问题(集群部署),可以设置过期时间让锁自动失效,但过期时间比较难设置。
缺点: 仍然不能达到可阻塞(同样可使用while true解决),可重入(记住主机和线程信息)
3.使用Zookeeper实现
获得锁:创建临时结点,看是不是结点是不是最小的,如果是则获得锁
释放锁:删除临时结点
优点: 解决单点故障,重新选择leader;可阻塞,其他试图再次创建该临时结点发现自己的结点不是最小值,会设置监听器,一旦发现列表有变化则检查是否自己为最小的结点;可重入,可以将主机和线程信息作为临时结点的值;如果客户端断开连接,临时结点会自动删除,从而不释放锁。
缺点: 因为要创建和删除临时结点,而且只能通过leader服务器执行,性能可能不太好
原文:http://www.cnblogs.com/lruihan/p/6502679.html