本章聚焦于如何通过系统性优化实现网站快速响应,从多层级、多维度剖析了高性能架构的核心策略。
Cache-Control
和 ETag
控制缓存策略。高性能架构需通过前端→服务端→数据库的全链路优化,结合缓存、异步、分片等核心技术,将系统瓶颈逐层击破。关键设计原则包括:
LRU(Least Recently Used)算法是一种常用的缓存淘汰策略,其核心思想是“淘汰最久未被访问的数据”。当缓存空间不足时,优先移除最近最少被使用的数据。
通常使用 双向链表 + 哈希表 实现:
class ListNode:
def __init__(self, key=None, value=None):
self.key = key
self.value = value
self.prev = None
self.next = None
class LRUCache:
def __init__(self, capacity: int):
self.capacity = capacity
self.hashmap = {} # 哈希表存储 key -> ListNode 映射
# 初始化双向链表头尾哨兵节点
self.head = ListNode()
self.tail = ListNode()
self.head.next = self.tail
self.tail.prev = self.head
def _remove_node(self, node: ListNode):
"""从链表中删除指定节点"""
node.prev.next = node.next
node.next.prev = node.prev
def _add_to_tail(self, node: ListNode):
"""将节点添加到链表尾部(标记为最近使用)"""
node.prev = self.tail.prev
node.next = self.tail
self.tail.prev.next = node
self.tail.prev = node
def get(self, key: int) -> int:
"""获取缓存数据"""
if key not in self.hashmap:
return -1
node = self.hashmap[key]
self._remove_node(node) # 从原位置删除
self._add_to_tail(node) # 移动到尾部(最新)
return node.value
def put(self, key: int, value: int) -> None:
"""插入/更新缓存数据"""
if key in self.hashmap:
node = self.hashmap[key]
node.value = value # 更新值
self._remove_node(node)
self._add_to_tail(node)
else:
if len(self.hashmap) >= self.capacity:
# 删除链表头部节点(最久未使用)
lru_node = self.head.next
self._remove_node(lru_node)
del self.hashmap[lru_node.key]
# 创建新节点并添加到尾部
new_node = ListNode(key, value)
self.hashmap[key] = new_node
self._add_to_tail(new_node)
# 测试示例
cache = LRUCache(2)
cache.put(1, 1)
cache.put(2, 2)
print(cache.get(1)) # 输出 1(此时 key=1 变为最近使用)
cache.put(3, 3) # 容量已满,淘汰 key=2
print(cache.get(2)) # 输出 -1(已被淘汰)
_remove_node
:删除节点。_add_to_tail
:将节点插入链表尾部(表示最近使用)。get
:若 key 存在,将节点移动到链表尾部。put
:若 key 存在则更新并移动;否则新增节点,若容量满则删除头节点。Memcached 是一个高性能的分布式内存缓存系统,主要用于缓存数据库查询结果、API响应等数据,以减轻后端数据库压力,提升应用响应速度。以下是它的核心功能、用法以及与 Redis 的对比:
# Ubuntu
apt-get install memcached
# CentOS
yum install memcached
memcached -d -m 1024 -p 11211 -u nobody -l 127.0.0.1
import memcache
# 连接 Memcached 集群(多个节点逗号分隔)
mc = memcache.Client(['127.0.0.1:11211', '127.0.0.1:11212'], debug=0)
# 写入数据(过期时间 60 秒)
mc.set("user:1001", {"name": "Alice", "age": 30}, time=60)
# 读取数据
data = mc.get("user:1001")
print(data) # 输出:{'name': 'Alice', 'age': 30}
# 删除数据
mc.delete("user:1001")
特性 | Memcached | Redis |
---|---|---|
数据结构 | 仅支持字符串(String) | 支持字符串、哈希、列表、集合、有序集合等 |
持久化 | 不支持,重启数据丢失 | 支持 RDB 快照和 AOF 日志 |
分布式实现 | 客户端分片(如一致性哈希) | 服务端分片(Redis Cluster) |
线程模型 | 多线程(高并发场景性能更优) | 单线程(避免锁竞争,原子性更强) |
内存管理 | 预分配内存池,碎片少 | 动态分配,支持内存淘汰策略 |
适用场景 | 简单键值缓存(高并发读) | 缓存、消息队列、实时排行榜等复杂场景 |
Memcached 是轻量级缓存解决方案,适合简单键值缓存和高并发读场景;Redis 功能更全面,适合需要持久化和复杂操作的场景。实际选型需结合业务需求(数据结构、持久化、性能)综合评估。
Redis 的分布式存储方案和集群分片方式是实现高可用性和水平扩展的核心技术,以下从 分布式方案 和 分片方式 两个维度详细说明:
MULTI
+ EXEC
在同一节点执行)。CRC16(key) % 16384
。# 查看 Key 所属槽位
redis-cli -c CLUSTER KEYSLOT "user:1001"
# 手动迁移槽(将槽 1000 从节点 A 迁移到节点 B)
redis-cli --cluster reshard <host:port> --cluster-from <node-A-id> --cluster-to <node-B-id> --cluster-slots 1000
user:0001~user:1000
归节点 A)。KEYS user:1*
)。Redis 的主从模式(Replication)和哨兵模式(Sentinel)本身并不提供数据分片(Sharding)功能,它们的设计目标主要是实现数据冗余、读写分离和高可用性。如果要实现数据分片(即分布式存储),需要结合其他分片策略或工具。以下是详细说明:
主从模式:
哨兵模式:
如果需要在主从或哨兵架构下实现分片,需借助外部方案,常见方法如下:
# 客户端代码示例(伪代码)
shard_nodes = [
{"master": "redis-node1:6379", "slaves": ["redis-node1-slave:6379"]},
{"master": "redis-node2:6379", "slaves": ["redis-node2-slave:6379"]},
]
def get_shard(key):
hash_value = crc32(key) % len(shard_nodes)
return shard_nodes[hash_value]
# 写入数据时选择对应的主节点
shard = get_shard("user:1001")
redis_client = Redis(shard["master"])
redis_client.set("user:1001", "Alice")
客户端 → Twemproxy → [主从集群1, 主从集群2, ...]
↓
Sentinel 监控每个主从集群
若需同时实现分片和高可用,应直接使用 Redis Cluster(官方集群方案):
建议:若数据量超出单节点内存,直接使用 Redis Cluster,避免在主从/哨兵模式上自行实现分片。