面试题答案
一键面试- 保证一致性的同步策略
- 读写时更新策略
- 写后更新:当MySQL数据发生变化(如执行INSERT、UPDATE、DELETE操作)后,立即更新Redis中的预计算结果。例如,假设在MySQL中有一个用户表
users
,统计用户总数作为Redis预计算数据。当执行INSERT INTO users (name, age) VALUES ('John', 25)
操作后,在MySQL事务提交后,立即调用Redis的INCR
命令(假设Redis中存储用户总数的键为user_count
)来更新用户总数。 - 写前更新:在MySQL执行写操作前,先更新Redis预计算结果。这种方式能避免在MySQL写操作过程中Redis数据不一致的短暂窗口。但需要注意,如果MySQL写操作失败,需要有回滚机制来恢复Redis数据。例如,在执行
UPDATE users SET age = age + 1 WHERE name = 'John'
前,先在Redis中更新与John
相关的年龄统计信息。
- 写后更新:当MySQL数据发生变化(如执行INSERT、UPDATE、DELETE操作)后,立即更新Redis中的预计算结果。例如,假设在MySQL中有一个用户表
- 异步更新策略
- 基于消息队列:使用消息队列(如Kafka、RabbitMQ)。当MySQL数据变化时,数据库触发器或应用程序代码发送一条消息到消息队列。消息内容包含数据变化的相关信息,如操作类型(INSERT、UPDATE、DELETE)以及涉及的数据。一个消费者程序监听这个消息队列,当接收到消息时,根据消息内容更新Redis中的预计算结果。例如,当MySQL的订单表
orders
插入一条新订单记录,应用程序发送一条消息到Kafka,消息包含新订单的金额等信息。消费者程序从Kafka读取消息,更新Redis中订单总金额等预计算数据。 - 基于MySQL Binlog:MySQL的二进制日志(Binlog)记录了数据库的所有写操作。可以使用工具(如Canal)模拟MySQL从库连接主库,解析Binlog中的数据变化。当解析到数据变化时,根据规则更新Redis预计算结果。例如,Canal解析到Binlog中关于产品表
products
的价格更新操作,根据这个更新操作在Redis中更新与产品相关的价格统计数据。
- 基于消息队列:使用消息队列(如Kafka、RabbitMQ)。当MySQL数据变化时,数据库触发器或应用程序代码发送一条消息到消息队列。消息内容包含数据变化的相关信息,如操作类型(INSERT、UPDATE、DELETE)以及涉及的数据。一个消费者程序监听这个消息队列,当接收到消息时,根据消息内容更新Redis中的预计算结果。例如,当MySQL的订单表
- 读写时更新策略
- 高效更新Redis预计算结果的技术手段
- 批量操作:避免频繁的单个Redis操作,尽量进行批量操作。例如,如果一次有多个用户的信息更新,将这些更新操作批量组合起来,使用Redis的
MSET
、MGET
等批量命令来减少网络开销。假设要更新多个用户的积分,可将所有用户的积分更新操作组合成一个MSET
命令,如MSET user:1:score 100 user:2:score 200 user:3:score 300
。 - 数据结构优化:选择合适的Redis数据结构。对于统计分析,
Hash
结构可用于存储多个相关的预计算结果。例如,对于每个产品的统计信息,可使用Hash
结构,键为product:product_id:stats
,字段为total_sales
、average_rating
等,值为对应的统计值。这样在更新时,可通过HSET
命令高效地更新单个字段,而不需要重新计算和设置整个键值对。 - 使用Lua脚本:Lua脚本在Redis中是原子执行的。可以将复杂的更新逻辑封装在Lua脚本中,减少网络往返次数并保证操作的原子性。例如,假设更新Redis中的库存统计时,需要同时更新总库存、可用库存等多个相关数据,可编写一个Lua脚本,在脚本中完成所有这些更新操作,然后通过
EVAL
命令在Redis中执行该脚本。
- 批量操作:避免频繁的单个Redis操作,尽量进行批量操作。例如,如果一次有多个用户的信息更新,将这些更新操作批量组合起来,使用Redis的
-- 假设键为stock:product_id,字段为total_stock和available_stock
local key = KEYS[1]
local delta = ARGV[1]
redis.call('HINCRBY', key, 'total_stock', delta)
redis.call('HINCRBY', key, 'available_stock', delta)
return'result'
通过redis-cli EVAL "脚本内容" 1 stock:1 10
(这里1表示传递1个键参数,stock:1
为键,10为库存变化量)来执行脚本。