面试题答案
一键面试常见竞争问题
- 数据不一致:多个客户端同时对列表进行插入、删除或修改操作时,可能导致最终数据状态与预期不符。例如,两个客户端同时执行
LPUSH
操作向列表头部插入元素,由于并发执行,可能出现插入顺序错乱,导致列表元素顺序并非按预期的先后顺序排列。 - 丢失更新:当多个客户端获取列表数据,在本地进行修改后再写回时,可能发生丢失更新问题。比如一个客户端从列表中
RPOP
一个元素,处理后再LPUSH
回列表,与此同时另一个客户端也RPOP
了同一个元素(因为第一个客户端还未LPUSH
回去),然后处理并LPUSH
回列表,这样第一个客户端的更新就被覆盖了,造成数据丢失。
解决思路和方法
- 使用事务(MULTI - EXEC):将多个操作包装在一个事务中,保证这些操作要么全部成功执行,要么全部不执行。Redis 的事务是原子性的,在事务执行期间不会被其他客户端的命令打断。例如:
MULTI
LPUSH mylist "element1"
RPUSH mylist "element2"
EXEC
- 使用乐观锁:通过
WATCH
命令监控一个或多个键,在执行事务前检查被监控的键是否被其他客户端修改。如果被修改,事务将被取消。例如:
WATCH mylist
MULTI
LPOP mylist
EXEC
如果在 WATCH
之后,EXEC
之前,mylist
被其他客户端修改,EXEC
会返回 nil
,表示事务执行失败,客户端需要重新获取数据并重新执行事务。
3. 使用队列(如 Redis 的 List 作为队列):将并发操作转换为顺序操作,通过 LPUSH
和 RPOP
等操作实现任务队列。例如,多个客户端将任务 LPUSH
到一个列表,一个工作进程不断从列表 RPOP
任务并处理,这样就避免了并发操作带来的竞争问题。
4. 使用分布式锁:可以使用 SETNX
(Set if Not eXists)命令实现简单的分布式锁。例如,客户端在执行对列表的操作前,先尝试获取锁:
SETNX mylock "unique_value"
如果返回 1
,表示获取锁成功,可以执行对列表的操作,操作完成后释放锁:
DEL mylock
如果返回 0
,表示锁已被其他客户端获取,等待一段时间后重试。