事务:分布式锁:修订间差异
imported>Soleverlee 以“=基于数据库= =基于redis/memcached= redis分布式锁即可以结合zk分布式锁锁高度安全和memcached并发场景下效率很好的优点,其实现...”为内容创建页面 |
imported>Soleverlee |
||
第1行: | 第1行: | ||
=基于数据库= | =基于数据库= | ||
可以基于数据库主键或者行级锁。在查询语句后面增加for update,数据库会在查询过程中给数据库表增加排他锁(这里再多提一句,InnoDB引擎在加锁的时候,只有通过索引进行检索的时候才会使用行级锁,否则会使用表级锁。这里我们希望使用行级锁,就要给method_name添加索引,值得注意的是,这个索引一定要创建成唯一索引,否则会出现多个重载方法之间无法同时被访问的问题。重载方法的话建议把参数类型也加上。)。当某条记录被加上排他锁之后,其他线程无法再在该行记录上增加排他锁。 | |||
<source lang="java"> | |||
public boolean lock(){ | |||
connection.setAutoCommit(false) | |||
while(true){ | |||
try{ | |||
result = select * from methodLock where method_name=xxx for update; | |||
if(result==null){ | |||
return true; | |||
} | |||
}catch(Exception e){ | |||
} | |||
sleep(1000); | |||
} | |||
return false; | |||
} | |||
public void unlock(){ | |||
connection.commit(); | |||
} | |||
</source> | |||
这里还可能存在另外一个问题,虽然我们对method_name 使用了唯一索引,并且显示使用for update来使用行级锁。但是,MySql会对查询进行优化,即便在条件中使用了索引字段,但是否使用索引来检索数据是由 MySQL 通过判断不同执行计划的代价来决定的,如果 MySQL 认为全表扫效率更高,比如对一些很小的表,它就不会使用索引,这种情况下 InnoDB 将使用表锁,而不是行锁。如果发生这种情况就悲剧了。。。 | |||
还有一个问题,就是我们要使用排他锁来进行分布式锁的lock,那么一个排他锁长时间不提交,就会占用数据库连接。一旦类似的连接变得多了,就可能把数据库连接池撑爆 | |||
=基于redis/memcached= | =基于redis/memcached= |
2016年12月19日 (一) 01:35的版本
基于数据库
可以基于数据库主键或者行级锁。在查询语句后面增加for update,数据库会在查询过程中给数据库表增加排他锁(这里再多提一句,InnoDB引擎在加锁的时候,只有通过索引进行检索的时候才会使用行级锁,否则会使用表级锁。这里我们希望使用行级锁,就要给method_name添加索引,值得注意的是,这个索引一定要创建成唯一索引,否则会出现多个重载方法之间无法同时被访问的问题。重载方法的话建议把参数类型也加上。)。当某条记录被加上排他锁之后,其他线程无法再在该行记录上增加排他锁。
public boolean lock(){
connection.setAutoCommit(false)
while(true){
try{
result = select * from methodLock where method_name=xxx for update;
if(result==null){
return true;
}
}catch(Exception e){
}
sleep(1000);
}
return false;
}
public void unlock(){
connection.commit();
}
这里还可能存在另外一个问题,虽然我们对method_name 使用了唯一索引,并且显示使用for update来使用行级锁。但是,MySql会对查询进行优化,即便在条件中使用了索引字段,但是否使用索引来检索数据是由 MySQL 通过判断不同执行计划的代价来决定的,如果 MySQL 认为全表扫效率更高,比如对一些很小的表,它就不会使用索引,这种情况下 InnoDB 将使用表锁,而不是行锁。如果发生这种情况就悲剧了。。。
还有一个问题,就是我们要使用排他锁来进行分布式锁的lock,那么一个排他锁长时间不提交,就会占用数据库连接。一旦类似的连接变得多了,就可能把数据库连接池撑爆
基于redis/memcached
redis分布式锁即可以结合zk分布式锁锁高度安全和memcached并发场景下效率很好的优点,其实现方式和memcached类似,采用setnx即可实现。需要注意的是,这里的redis也需要设置超时时间。以避免死锁。
memcached带有add函数,利用add函数的特性即可实现分布式锁。add和set的区别在于:如果多线程并发set,则每个set都会成功,但最后存储的值以最后的set的线程为准。而add的话则相反,add会添加第一个到达的值,并返回true,后续的添加则都会返回false。利用该点即可很轻松地实现分布式锁
缺点:
- memcached采用列入LRU置换策略,所以如果内存不够,可能导致缓存中的锁信息丢失;
- memcached无法持久化,一旦重启,将导致信息丢失。
基于Zookeeper
基于zookeeper瞬时有序节点实现的分布式锁,大致思想即为:每个客户端对某个功能加锁时,在zookeeper上的与该功能对应的指定节点的目录下,生成一个唯一的瞬时有序节点。判断是否获取锁的方式很简单,只需要判断有序节点中序号最小的一个。当释放锁的时候,只需将这个瞬时节点删除即可。同时,其可以避免服务宕机导致的锁无法释放,而产生的死锁问题。
可以直接采用zookeeper第三方库curator即可方便地实现分布式锁