面试题答案
一键面试设计与实现思路
- 数据结构
- 哈希表:用于存储缓存数据,在Ruby中可以直接使用
Hash
。键值对形式存储,键通常为缓存数据的标识(如URL、数据库查询语句等),值为实际缓存的数据。例如:
cache = {} cache['user:1'] = {name: 'John', age: 30}
- 哈希表:用于存储缓存数据,在Ruby中可以直接使用
- 算法
- 一致性哈希算法:解决缓存服务器分布和数据映射问题。
- 原理:将所有可能的键映射到一个固定的哈希空间(如0 - 2^32 - 1),每个缓存服务器也在这个哈希空间中有一个哈希值作为其位置。当有数据需要缓存时,先计算数据键的哈希值,然后沿哈希环顺时针查找,找到第一个缓存服务器节点来存储该数据。
- 实现:在Ruby中可以使用
Digest::MD5
等哈希算法库来计算哈希值。例如:
require 'digest' def hash_key(key) Digest::MD5.hexdigest(key).to_i(16) end
- 缓存淘汰算法:
- LRU(最近最少使用):当缓存满时,淘汰最久未使用的数据。可以使用双向链表和哈希表结合实现。双向链表记录数据的访问顺序,哈希表用于快速定位数据在链表中的位置。在Ruby中,可以自己实现双向链表结构,也可以使用第三方库如
linkedlist
。
require 'linkedlist' class LRUCache def initialize(capacity) @capacity = capacity @cache = {} @list = LinkedList.new end def get(key) if @cache.key?(key) node = @cache[key] @list.move_to_front(node) node.value else nil end end def put(key, value) if @cache.key?(key) node = @cache[key] node.value = value @list.move_to_front(node) else new_node = @list.push_front(value) @cache[key] = new_node if @list.size > @capacity removed_node = @list.pop_back @cache.delete(removed_node.key) end end end end
- LRU(最近最少使用):当缓存满时,淘汰最久未使用的数据。可以使用双向链表和哈希表结合实现。双向链表记录数据的访问顺序,哈希表用于快速定位数据在链表中的位置。在Ruby中,可以自己实现双向链表结构,也可以使用第三方库如
- 一致性哈希算法:解决缓存服务器分布和数据映射问题。
- 通信协议
- HTTP:简单通用,适合分布式系统间通信。在Ruby中可以使用
Net::HTTP
库进行HTTP请求。例如,向缓存服务器获取数据:
require 'net/http' uri = URI('http://cache - server:port/cache/user:1') response = Net::HTTP.get(uri)
- TCP:性能更高,适合对性能要求较高的场景。在Ruby中可以使用
TCPSocket
进行TCP连接通信。例如:
require 'socket' socket = TCPSocket.open('cache - server', port) socket.puts('GET user:1') data = socket.gets socket.close
- HTTP:简单通用,适合分布式系统间通信。在Ruby中可以使用
- 保证一致性
- 写操作:
- 同步更新:当一个节点更新缓存数据时,通过广播或多播的方式通知其他节点更新。可以使用消息队列(如RabbitMQ),当有缓存更新时,发布一条更新消息到队列,其他节点订阅该队列,收到消息后更新本地缓存。
- 版本控制:给每个缓存数据添加版本号,每次更新版本号递增。当读取数据时,不仅返回数据,还返回版本号。客户端再次读取时,比较版本号,若不一致则重新获取。例如:
cache['user:1'] = {data: {name: 'John', age: 30}, version: 1}
- 读操作:
- 缓存穿透:使用布隆过滤器,在查询缓存前先通过布隆过滤器判断数据是否存在,若不存在则直接返回,避免无效查询穿透到后端数据源。在Ruby中可以使用
bloomfilter - rb
库。 - 缓存雪崩:设置缓存过期时间时,添加随机的时间偏移,避免大量缓存同时过期。例如:
expiration_time = Time.now + 3600 + rand(600)
- 缓存穿透:使用布隆过滤器,在查询缓存前先通过布隆过滤器判断数据是否存在,若不存在则直接返回,避免无效查询穿透到后端数据源。在Ruby中可以使用
- 写操作:
- 保证高可用性
- 主从复制:设置主缓存服务器和多个从缓存服务器,主服务器负责写操作,写操作完成后同步数据到从服务器。从服务器负责读操作,当主服务器故障时,从服务器可以晋升为主服务器。可以使用
Redis
等支持主从复制的工具,在Ruby中通过redis - rb
库连接和操作。 - 集群化:采用分布式集群方案,如
Consul
等工具实现服务发现和健康检查。每个缓存服务器节点向Consul
注册自己,当某个节点故障时,Consul
通知其他节点,其他节点重新调整缓存数据的分布。在Ruby中可以使用consul - rb
库与Consul
进行交互。
- 主从复制:设置主缓存服务器和多个从缓存服务器,主服务器负责写操作,写操作完成后同步数据到从服务器。从服务器负责读操作,当主服务器故障时,从服务器可以晋升为主服务器。可以使用