面试题答案
一键面试锁的获取策略优化
- 减少无效重试:
- 在获取锁之前,先通过Lua脚本判断锁是否有可能在短时间内被释放。例如,检查锁的过期时间,如果距离过期时间很短(如小于100毫秒),则等待一小段时间(如50毫秒)后再尝试获取锁,避免大量无效的重试。
- 示例:
local lock_key = KEYS[1] local lock_value = ARGV[1] local expire_time = ARGV[2] local current_time = redis.call('TIME')[1] local remaining_time = redis.call('PTTL', lock_key) if remaining_time < 100 then -- 这里模拟等待50毫秒 redis.call('SLEEP', 0.05) end local result = redis.call('SETNX', lock_key, lock_value) if result == 1 then redis.call('PEXPIRE', lock_key, expire_time) end return result
- 使用公平锁策略:引入一个队列来记录请求锁的顺序,按照先进先出的原则获取锁。可以使用Redis的List数据结构实现。每个请求锁的客户端生成一个唯一标识,将其加入到List中。获取锁时,先检查自己的标识是否在List头部,如果是则获取锁,否则等待。
- 示例:
import redis r = redis.Redis(host='localhost', port=6379, db = 0) request_id = 'unique_id_1' r.rpush('lock_request_queue', request_id) while True: head_id = r.lindex('lock_request_queue', 0) if head_id.decode('utf-8') == request_id: if r.setnx('my_lock', 'lock_value'): r.lpop('lock_request_queue') break else: time.sleep(0.1)
重试算法优化
- 指数退避重试算法:随着重试次数的增加,等待时间以指数级增长。例如,第一次重试等待100毫秒,第二次等待200毫秒,第三次等待400毫秒等。这样可以避免过于频繁的重试对系统造成压力。
- 示例(Python):
import redis import time r = redis.Redis(host='localhost', port=6379, db = 0) max_retries = 5 base_delay = 0.1 for i in range(max_retries): if r.setnx('my_lock', 'lock_value'): break delay = base_delay * (2 ** i) time.sleep(delay)
- 随机化退避算法:在指数退避的基础上,加入一定的随机性。例如,每次等待时间在指数退避的时间基础上,加上一个随机值(如0到100毫秒之间)。这样可以避免多个客户端同时重试导致的“惊群效应”。
- 示例(Python):
import redis import time import random r = redis.Redis(host='localhost', port=6379, db = 0) max_retries = 5 base_delay = 0.1 for i in range(max_retries): if r.setnx('my_lock', 'lock_value'): break delay = base_delay * (2 ** i) + random.uniform(0, 0.1) time.sleep(delay)
缓存淘汰策略优化
- 设置合理的锁过期时间:根据业务场景,设置一个合适的锁过期时间。如果过期时间过短,可能导致业务未完成锁就被释放;如果过长,可能会影响其他客户端获取锁的效率。例如,对于一些短时间的业务操作,如订单生成,设置5秒的过期时间。
- 在获取锁时设置过期时间:
import redis r = redis.Redis(host='localhost', port=6379, db = 0) if r.setnx('order_lock', 'lock_value'): r.expire('order_lock', 5)
- 采用主动释放锁和过期释放锁结合:客户端在业务完成后主动释放锁,同时设置锁的过期时间作为兜底。这样可以确保即使客户端出现异常没有主动释放锁,也不会导致锁永远无法被其他客户端获取。
- 示例(Python):
import redis r = redis.Redis(host='localhost', port=6379, db = 0) if r.setnx('my_lock', 'lock_value'): r.expire('my_lock', 10) try: # 业务逻辑 pass finally: r.delete('my_lock')