行级锁
行级锁指对某行数据加锁,是一种排他锁,防止其他事务修改此行。在执行以下数据库操作时,数据库会自动应用行级锁。
- 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会自动释放锁给其他线程使用:
public class RedisLock {
private final static Log logger = LogFactory.getLog(BuilderDemo.class);
private Jedis jedis;
public RedisLock(Jedis jedis) {
this.jedis = jedis;
}
//获取锁
public synchronized boolean lock(String lockId) {
//设置锁
Long status = jedis.setnx(lockId, System.currentTimeMillis() + "");
if (0 == status) {//有人在使用该锁,获取锁失败
return false;
} else {
return true;//创建、获取锁成功,锁id=lockId
}
}
//释放锁
public synchronized boolean unlock(String lockId) {
String lockValue = jedis.get(lockId);
if (lockValue != null) {//释放锁成功
jedis.del(lockId);
return true;
} else {
return false;//释放锁失败
}
}
public static void main(String[] args) {
JedisPoolConfig jcon = new JedisPoolConfig();
JedisPool jp = new JedisPool(jcon, "127.0.0.1", 6379);
Jedis jedis = jp.getResource();
RedisLock lock = new RedisLock(jedis);
String lockId = "123";
try {
if (lock.lock(lockId)) {
//加锁后需要执行的逻辑代码"
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock(lockId);
}
}
}
以上代码定义了RedisLock类,在该类中定义了一个Redis数据库连接Jedis,同时定义了lock方法来获取一个锁,在获取锁时首先通过setnx设置锁id获取Redis内锁的信息,如果返回信息为 0,则表示锁正在被人使用(锁id存在于Redis中);如果不为 0,则表示成功在内存中设置了该锁。同时在RedisLock类中定义了unlock方法用于释放一个锁,具体做法是在Redis中查找该锁并删除。