面试题答案
一键面试处理MySQL操作异常时的Redis锁
- 处理流程:
- 捕获异常:在执行MySQL操作的代码块中,使用try - catch语句捕获可能发生的异常。例如在Java中:
try {
// 获取Redis锁
boolean lockAcquired = jedis.set(lockKey, requestId, "NX", "EX", lockExpirySeconds) != null;
if (lockAcquired) {
// 执行MySQL操作
// 例如使用JDBC执行SQL语句
Connection conn = DriverManager.getConnection(url, username, password);
Statement stmt = conn.createStatement();
stmt.executeUpdate("INSERT INTO your_table (column1, column2) VALUES ('value1', 'value2')");
}
} catch (SQLException e) {
// 处理MySQL操作异常
// 释放Redis锁
if (lockAcquired) {
jedis.del(lockKey);
}
// 可以选择记录异常日志等操作
e.printStackTrace();
}
- **释放锁**:一旦捕获到异常,立即检查是否成功获取了Redis锁。如果获取了锁,则调用Redis的删除操作来释放锁,确保不会发生死锁。
2. 用到的Redis特性:
- SET命令的NX和EX选项:SET key value NX EX seconds
用于获取锁。NX
表示只有当键不存在时才设置键值对,保证了锁的唯一性;EX seconds
为锁设置过期时间,防止因程序崩溃等异常情况导致锁无法释放而产生死锁。
- DEL命令:用于释放锁,即删除存储锁的键。
处理网络波动导致获取锁响应延迟的情况
- 保证原子性和一致性的方法:
- 基于Redis的SETNX操作:多个客户端同时尝试获取锁时,由于Redis的单线程特性,
SET key value NX
操作是原子的。只有一个客户端能成功设置键值对,即获取到锁。 - 锁的过期时间和重试机制:设置合理的锁过期时间,即使某个客户端在获取锁后因网络波动长时间未响应,锁也会自动过期释放。其他客户端可以重新尝试获取锁。客户端在获取锁失败后,可以按照一定的策略进行重试,例如指数退避算法。在Java中可以这样实现重试逻辑:
- 基于Redis的SETNX操作:多个客户端同时尝试获取锁时,由于Redis的单线程特性,
int retryCount = 0;
while (true) {
boolean lockAcquired = jedis.set(lockKey, requestId, "NX", "EX", lockExpirySeconds) != null;
if (lockAcquired) {
try {
// 执行MySQL操作
break;
} catch (SQLException e) {
// 处理MySQL操作异常
jedis.del(lockKey);
e.printStackTrace();
}
} else {
try {
// 指数退避等待时间
int waitTime = (int) Math.pow(2, retryCount) * 100;
Thread.sleep(waitTime);
retryCount++;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
- **唯一标识(Request ID)**:每个客户端在获取锁时,设置一个唯一的标识(如UUID)作为锁的值。在释放锁时,先检查锁的值是否与自己设置的标识一致,只有一致时才释放锁,避免误释放其他客户端的锁,保证数据一致性。例如在释放锁时:
if (requestId.equals(jedis.get(lockKey))) {
jedis.del(lockKey);
}