面试题答案
一键面试Redis 配置优化
- 增加连接池大小 在 Redis 配置中,增加客户端连接池的大小。例如在 Jedis 中,可以通过如下方式设置连接池参数:
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(100); // 最大连接数
poolConfig.setMaxIdle(20); // 最大空闲连接数
JedisPool jedisPool = new JedisPool(poolConfig, "localhost", 6379);
这样可以让更多客户端同时与 Redis 交互,减少因为连接不足导致的竞争。
2. 调整 Redis 内存策略
根据业务需求合理设置 Redis 的内存淘汰策略,如 volatile - lru
(对设置了过期时间的 key 使用 LRU 算法淘汰)。在 redis.conf
文件中配置:
maxmemory-policy volatile - lru
避免因为内存不足导致的异常,影响 WATCH 命令执行。
客户端代码设计优化
- 重试机制
在客户端代码中,当捕获到 WATCH 命令错误(如
WatchError
)时,进行重试。以 Python 的 redis - py 库为例:
import redis
r = redis.Redis(host='localhost', port=6379, db = 0)
retry_count = 3
while True:
try:
pipe = r.pipeline()
pipe.watch('key')
value = pipe.get('key')
new_value = int(value) + 1 if value else 1
pipe.multi()
pipe.set('key', new_value)
pipe.execute()
break
except redis.WatchError:
if retry_count <= 0:
raise
retry_count -= 1
- 减少事务中的操作 尽量减少 WATCH 事务中包含的命令数量,降低冲突的概率。例如,如果一个事务中有多个写操作,可以拆分成多个较小的事务。
// 优化前
Jedis jedis = jedisPool.getResource();
Transaction tx = jedis.multi();
tx.watch("key1", "key2", "key3");
tx.set("key1", "value1");
tx.set("key2", "value2");
tx.set("key3", "value3");
tx.exec();
jedis.close();
// 优化后
Jedis jedis = jedisPool.getResource();
Transaction tx1 = jedis.multi();
tx1.watch("key1");
tx1.set("key1", "value1");
tx1.exec();
Transaction tx2 = jedis.multi();
tx2.watch("key2");
tx2.set("key2", "value2");
tx2.exec();
Transaction tx3 = jedis.multi();
tx3.watch("key3");
tx3.set("key3", "value3");
tx3.exec();
jedis.close();
整体架构优化
- 分布式锁替代 在某些场景下,可以使用分布式锁(如 Redisson 实现的分布式锁)来替代 WATCH 机制。例如在 Java 项目中使用 Redisson:
Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
RedissonClient redisson = Redisson.create(config);
RLock lock = redisson.getLock("myLock");
try {
lock.lock();
// 执行读写操作
Jedis jedis = jedisPool.getResource();
String value = jedis.get("key");
jedis.set("key", "newValue");
jedis.close();
} finally {
lock.unlock();
redisson.shutdown();
}
- 读写分离 采用读写分离架构,读操作从从节点获取数据,写操作在主节点进行。例如在 Sentinel 模式下,客户端通过 Sentinel 发现主从节点,分别进行读写:
Set<String> sentinels = new HashSet<>(Arrays.asList("127.0.0.1:26379"));
JedisSentinelPool jedisSentinelPool = new JedisSentinelPool("mymaster", sentinels);
// 读操作
Jedis jedisRead = jedisSentinelPool.getResource();
String value = jedisRead.get("key");
jedisRead.close();
// 写操作
Jedis jedisWrite = jedisSentinelPool.getResource();
jedisWrite.set("key", "newValue");
jedisWrite.close();
在实际项目中,以电商库存扣减场景为例:
- Redis 配置优化:增加连接池大小,确保在高并发下库存扣减操作有足够的连接与 Redis 交互。
- 客户端代码设计优化:对库存扣减操作的 WATCH 事务增加重试机制,避免因竞争导致库存扣减失败。
- 整体架构优化:采用分布式锁替代 WATCH 机制来扣减库存,同时结合读写分离,将库存查询操作分发到从节点,减少主节点压力,提高系统的性能和可用性。