面试题答案
一键面试利用Redis锁机制防止MySQL重复插入数据
- 加锁步骤:
- 使用Redis的
SETNX
(SET if Not eXists)命令来尝试获取锁。例如,在Python中使用redis - py
库:
- 使用Redis的
import redis
r = redis.Redis(host='localhost', port=6379, db = 0)
lock_key = 'unique_insert_lock'
lock_value = 'unique_value'
is_lock_acquired = r.setnx(lock_key, lock_value)
- `SETNX`命令只有在键不存在时才会设置键的值,返回1表示成功获取锁,返回0表示锁已被其他进程持有。
2. 解锁步骤: - 当数据插入完成后,需要释放锁。同样在Python中:
if is_lock_acquired:
try:
# 执行MySQL插入操作
pass
finally:
r.delete(lock_key)
- 使用`DELETE`命令删除锁对应的键,以释放锁供其他进程获取。
可能遇到的问题及解决方案
- 死锁问题:
- 问题描述:如果获取锁的进程在持有锁期间崩溃,没有执行解锁操作,那么该锁将永远不会被释放,导致其他进程无法获取锁,形成死锁。
- 解决方案:为锁设置过期时间。在获取锁时,可以使用
SET
命令并带上EX
(过期时间,单位秒)选项。例如:
is_lock_acquired = r.set(lock_key, lock_value, ex = 10, nx = True)
- 这样即使进程崩溃,10秒后锁也会自动释放。
2. 锁误删问题: - 问题描述:假设进程A获取了锁,在执行操作的过程中,锁过期自动释放了,此时进程B获取到了锁。然后进程A执行完操作准备解锁,却把进程B的锁给删除了。 - 解决方案:在获取锁时设置一个唯一的标识(如UUID)作为锁的值。解锁时,先验证锁的值是否与自己设置的标识一致,只有一致时才执行删除操作。例如:
import uuid
lock_value = str(uuid.uuid4())
is_lock_acquired = r.set(lock_key, lock_value, ex = 10, nx = True)
if is_lock_acquired:
try:
# 执行MySQL插入操作
pass
finally:
if r.get(lock_key) == lock_value.encode('utf - 8'):
r.delete(lock_key)