行级锁

行级锁指对某行数据加锁,是一种排他锁,防止其他事务修改此行。在执行以下数据库操作时,数据库会自动应用行级锁。

  • INSERT 、 UPDATE 、 DELETE 、 SELECT … FOR UPDATE [OF columns] [WAIT n|NOWAIT]。
  • SELECT … FOR UPDATE语句允许用户一次针对多条记录执行更新。
  • 使用COMMIT或ROLLBACK语句释放锁。

表级锁

表级锁指对当前操作的整张表加锁,它的实现简单,资源消耗较少,被大部分存储引擎支持。最常使用的MyISAM与InnoDB都支持表级锁定。表级锁定分为表共享读锁(共享锁)与表独占写锁(排他锁)。

页级锁

页级锁的锁定粒度介于行级锁和表级锁之间。表级锁的加锁速度快,但冲突多,行级冲突少,但加锁速度慢。页级锁在二者之间做了平衡,一次锁定相邻的一组记录。

基于Redis的分布式锁

数据库锁是基于单个数据库实现的,在我们的业务跨多个数据库时,就要使用分布式锁来保证数据的一致性。下面介绍使用Redis实现一个分布式锁的流程。Redis实现的分布式锁以Redis setnx命令为中心 实 现 , setnx 是 Redis 的 写 入 操 作 命 令 , 具 体 语 法 为 setnx(key val)。在且仅在key不存在时,则插入一个key为val的字符串,返回1;若key存在,则什么都不做,返回0。通过setnx实现分布式锁的思路如下。

  • 获取锁:在获取锁时调用setnx,如果返回 0,则该锁正在被别人使用;如果返回1,则成功获取锁。
  • 释放锁:在释放锁时,判断锁是否存在,如果存在,则执行Redis的delete操作释放锁。

简单的Redis实现分布式锁的代码如下,注意,如果锁并发比较大,则可以设置一个锁的超时时间,在超时时间到后,Redis会自动释放锁给其他线程使用:

  1. public class RedisLock {
  2. private final static Log logger = LogFactory.getLog(BuilderDemo.class);
  3. private Jedis jedis;
  4. public RedisLock(Jedis jedis) {
  5. this.jedis = jedis;
  6. }
  7. //获取锁
  8. public synchronized boolean lock(String lockId) {
  9. //设置锁
  10. Long status = jedis.setnx(lockId, System.currentTimeMillis() + "");
  11. if (0 == status) {//有人在使用该锁,获取锁失败
  12. return false;
  13. } else {
  14. return true;//创建、获取锁成功,锁id=lockId
  15. }
  16. }
  17. //释放锁
  18. public synchronized boolean unlock(String lockId) {
  19. String lockValue = jedis.get(lockId);
  20. if (lockValue != null) {//释放锁成功
  21. jedis.del(lockId);
  22. return true;
  23. } else {
  24. return false;//释放锁失败
  25. }
  26. }
  27. public static void main(String[] args) {
  28. JedisPoolConfig jcon = new JedisPoolConfig();
  29. JedisPool jp = new JedisPool(jcon, "127.0.0.1", 6379);
  30. Jedis jedis = jp.getResource();
  31. RedisLock lock = new RedisLock(jedis);
  32. String lockId = "123";
  33. try {
  34. if (lock.lock(lockId)) {
  35. //加锁后需要执行的逻辑代码"
  36. }
  37. } catch (Exception e) {
  38. e.printStackTrace();
  39. } finally {
  40. lock.unlock(lockId);
  41. }
  42. }
  43. }

以上代码定义了RedisLock类,在该类中定义了一个Redis数据库连接Jedis,同时定义了lock方法来获取一个锁,在获取锁时首先通过setnx设置锁id获取Redis内锁的信息,如果返回信息为 0,则表示锁正在被人使用(锁id存在于Redis中);如果不为 0,则表示成功在内存中设置了该锁。同时在RedisLock类中定义了unlock方法用于释放一个锁,具体做法是在Redis中查找该锁并删除。