一、一致性哈希基础回顾
1.1 基础概念
python
复制
下载
class ConsistentHashingBase:
"""一致性哈希基础"""
def __init__(self, nodes=None, virtual_nodes_per_node=100):
self.real_nodes = nodes or [] # 真实节点
self.virtual_nodes_per_node = virtual_nodes_per_node
self.hash_ring = {} # 哈希环:位置 -> 虚拟节点
self.virtual_to_real = {} # 虚拟节点 -> 真实节点映射
def basic_principles(self):
"""基本原理"""
principles = {
"目标": {
"分布式缓存": "缓存节点增减时最小化数据迁移",
"负载均衡": "将请求均匀分布到各节点",
"数据分片": "分布式数据库/存储系统"
},
"传统哈希问题": {
"问题": "hash(key) % N,N变化时几乎所有数据都需要重映射",
"示例": "N从3增加到4,75%的数据需要迁移",
"公式": "迁移比例 = 1 - 1/N_new"
},
"一致性哈希解决": {
"环形哈希空间": "将哈希值组织成环(0 ~ 2^32-1)",
"数据定位": "数据哈希后顺时针找到第一个节点",
"节点增减": "只影响相邻区域的数据"
}
}
return principles
二、虚拟节点机制详解
2.1 虚拟节点原理
python
复制
下载
class VirtualNodesMechanism:
"""虚拟节点机制"""
def __init__(self):
self.virtual_node_benefits = self._explain_virtual_nodes()
def _explain_virtual_nodes(self):
"""解释虚拟节点"""
return {
"问题背景(无虚拟节点)": {
"数据倾斜": {
"原因": "节点在环上分布不均匀",
"示例": "3个节点可能集中在环的某一段",
"后果": "某些节点负载过高"
},
"热点问题": {
"场景": "某些数据访问频率极高",
"影响": "所在节点成为瓶颈",
"解决": "难以动态调整"
},
"节点容量差异": {
"场景": "节点硬件配置不同",
"问题": "权重无法体现",
"后果": "小容量节点容易过载"
}
},
"虚拟节点解决方案": {
"基本思想": "每个物理节点对应多个虚拟节点",
"哈希环表现": "虚拟节点均匀分布在环上",
"数据映射": "数据映射到虚拟节点,再对应到物理节点",
"关键参数": {
"虚拟节点数": "通常100-1000个/物理节点",
"哈希函数": "确保虚拟节点均匀分布",
"命名规则": "node_ip:port#vnode_index"
},
"数学原理": {
"分布均匀性": "虚拟节点数→∞时,分布趋于均匀",
"负载均衡": "方差 ∝ 1/(虚拟节点数)",
"公式": "负载标准差 ≈ 总负载 / √(虚拟节点数×物理节点数)"
}
}
}
def create_virtual_nodes(self, real_nodes, virtual_factor=100):
"""创建虚拟节点"""
virtual_nodes_map = {}
for node in real_nodes:
node_id = f"{node['host']}:{node['port']}"
weight = node.get('weight', 1) # 节点权重
# 根据权重计算虚拟节点数
vnode_count = int(virtual_factor * weight)
for i in range(vnode_count):
# 虚拟节点命名
vnode_name = f"{node_id}#vnode_{i}"
# 计算虚拟节点在环上的位置
# 使用不同的哈希种子确保均匀
position = self._hash_with_seed(vnode_name, i)
virtual_nodes_map[position] = {
'vnode': vnode_name,
'real_node': node_id,
'weight': weight
}
return virtual_nodes_map
def _hash_with_seed(self, key, seed):
"""带种子的哈希函数"""
import hashlib
# 使用不同的种子创建不同的哈希值
data = f"{key}_{seed}".encode()
return int(hashlib.md5(data).hexdigest()[:8], 16) % (2**32)
2.2 虚拟节点实现
python
复制
下载
class EnhancedConsistentHash:
"""增强型一致性哈希(带虚拟节点)"""
def __init__(self, nodes=None, virtual_nodes_per_node=100,
hash_function='md5'):
"""
Args:
nodes: 真实节点列表
virtual_nodes_per_node: 每个真实节点的虚拟节点数
hash_function: 哈希函数类型
"""
self.real_nodes = {} # 真实节点信息
self.virtual_nodes = {} # 虚拟节点映射
self.sorted_positions = [] # 排序后的位置列表
self.virtual_nodes_per_node = virtual_nodes_per_node
# 初始化哈希函数
self.hash_func = self._get_hash_function(hash_function)
# 添加节点
if nodes:
for node in nodes:
self.add_node(node)
def _get_hash_function(self, func_name):
"""获取哈希函数"""
import hashlib
import mmh3
hash_functions = {
'md5': lambda x: int(hashlib.md5(x.encode()).hexdigest()[:8], 16),
'sha1': lambda x: int(hashlib.sha1(x.encode()).hexdigest()[:8], 16),
'murmur': lambda x: mmh3.hash(x) & 0xffffffff,
'crc32': lambda x: binascii.crc32(x.encode()) & 0xffffffff
}
return hash_functions.get(func_name, hash_functions['md5'])
def add_node(self, node_config):
"""添加真实节点"""
node_id = self._get_node_id(node_config)
# 记录真实节点
self.real_nodes[node_id] = {
'config': node_config,
'virtual_nodes': [],
'data_count': 0,
'weight': node_config.get('weight', 1)
}
# 创建虚拟节点
weight = node_config.get('weight', 1)
vnode_count = int(self.virtual_nodes_per_node * weight)
for i in range(vnode_count):
vnode_id = f"{node_id}#vnode_{i}"
# 使用不同策略生成虚拟节点位置
position = self._calculate_vnode_position(vnode_id, i)
# 记录虚拟节点
self.virtual_nodes[position] = {
'vnode_id': vnode_id,
'real_node': node_id,
'position': position
}
# 记录到真实节点
self.real_nodes[node_id]['virtual_nodes'].append(position)
# 重新排序位置
self._rebuild_sorted_positions()
print(f"添加节点 {node_id}, 权重 {weight}, 虚拟节点数 {vnode_count}")
return node_id
def _calculate_vnode_position(self, vnode_id, index):
"""计算虚拟节点位置"""
# 方法1:使用vnode_id哈希
pos1 = self.hash_func(vnode_id) % (2**32)
# 方法2:使用vnode_id + 盐值哈希(增加随机性)
salt = f"salt_{index}"
pos2 = self.hash_func(vnode_id + salt) % (2**32)
# 方法3:使用均匀分布公式(对于权重调整特别有用)
if index == 0:
pos3 = self.hash_func(vnode_id) % (2**32)
else:
# 在环上均匀分布
interval = (2**32) // self.virtual_nodes_per_node
base_pos = self.hash_func(vnode_id.split('#')[0]) % (2**32)
pos3 = (base_pos + index * interval) % (2**32)
# 返回组合结果(增加随机性)
return (pos1 ^ pos2 ^ pos3) % (2**32)
def remove_node(self, node_id):
"""移除真实节点"""
if node_id not in self.real_nodes:
raise ValueError(f"节点 {node_id} 不存在")
# 移除所有虚拟节点
for position in self.real_nodes[node_id]['virtual_nodes']:
del self.virtual_nodes[position]
# 移除真实节点
del self.real_nodes[node_id]
# 重新排序
self._rebuild_sorted_positions()
print(f"移除节点 {node_id}")
return True
def _rebuild_sorted_positions(self):
"""重建排序的位置列表"""
self.sorted_positions = sorted(self.virtual_nodes.keys())
def get_node(self, key):
"""根据key获取对应的真实节点"""
if not self.virtual_nodes:
return None
# 计算key的哈希值
key_hash = self.hash_func(str(key)) % (2**32)
# 二分查找找到第一个>=key_hash的位置
import bisect
pos_index = bisect.bisect_left(self.sorted_positions, key_hash)
# 环形处理
if pos_index == len(self.sorted_positions):
pos_index = 0
# 获取虚拟节点位置
vnode_position = self.sorted_positions[pos_index]
# 获取对应的真实节点
vnode_info = self.virtual_nodes[vnode_position]
real_node_id = vnode_info['real_node']
# 更新统计
self.real_nodes[real_node_id]['data_count'] += 1
return real_node_id
def analyze_distribution(self, test_keys=10000):
"""分析分布均匀性"""
from collections import Counter
# 模拟分配测试key
node_counter = Counter()
for i in range(test_keys):
key = f"test_key_{i}"
node_id = self.get_node(key)
node_counter[node_id] += 1
# 计算统计指标
total_keys = sum(node_counter.values())
expected_per_node = total_keys / len(self.real_nodes)
metrics = {
'total_nodes': len(self.real_nodes),
'total_virtual_nodes': len(self.virtual_nodes),
'total_keys': total_keys,
'distribution': {}
}
# 每个节点的统计
for node_id, count in node_counter.items():
deviation = abs(count - expected_per_node) / expected_per_node
load_percentage = count / total_keys * 100
metrics['distribution'][node_id] = {
'key_count': count,
'expected': round(expected_per_node, 2),
'deviation': f"{deviation*100:.2f}%",
'load_percentage': f"{load_percentage:.2f}%",
'virtual_nodes': len(self.real_nodes[node_id]['virtual_nodes'])
}
# 整体统计
import numpy as np
counts = list(node_counter.values())
metrics['overall'] = {
'std_dev': np.std(counts),
'cv': np.std(counts) / np.mean(counts) * 100, # 变异系数
'min_load': min(counts),
'max_load': max(counts),
'load_ratio': max(counts) / min(counts) if min(counts) > 0 else float('inf')
}
return metrics
篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc
需要全套面试笔记及答案
【点击此处即可/免费获取】
三、数据迁移机制
3.1 节点变化时的数据迁移
python
复制
下载
class DataMigrationStrategy:
"""数据迁移策略"""
def __init__(self):
self.migration_strategies = self._define_strategies()
def _define_strategies(self):
"""定义迁移策略"""
return {
"添加节点时的迁移": {
"受影响数据": "新节点在环上的前驱节点负责的部分数据",
"迁移比例": f"≈ 1/(N+1),N为原节点数",
"优化策略": {
"增量迁移": "分批次迁移,避免服务中断",
"双写策略": "迁移期间同时写入新旧节点",
"流量切换": "迁移完成后逐步切换流量"
}
},
"删除节点时的迁移": {
"受影响数据": "被删除节点的所有数据",
"迁移目标": "顺时针方向的下一个节点",
"迁移方式": {
"直接迁移": "全部数据迁移到后继节点",
"重新分布": "根据虚拟节点重新分布",
"延迟删除": "标记删除,逐步迁移"
}
},
"节点故障处理": {
"自动检测": "心跳检测,超时判定故障",
"快速切换": "虚拟节点自动映射到备份节点",
"数据恢复": "从副本或其他节点恢复数据",
"一致性保证": "确保迁移过程的数据一致性"
}
}
def calculate_migration_keys(self, old_ring, new_ring, keys):
"""计算需要迁移的key"""
migration_map = {}
for key in keys:
old_node = self._find_node_in_ring(key, old_ring)
new_node = self._find_node_in_ring(key, new_ring)
if old_node != new_node:
if new_node not in migration_map:
migration_map[new_node] = {'from': old_node, 'keys': []}
migration_map[new_node]['keys'].append(key)
return migration_map
def _find_node_in_ring(self, key, hash_ring):
"""在哈希环中查找节点"""
key_hash = self._hash_key(key)
positions = sorted(hash_ring.keys())
import bisect
pos_index = bisect.bisect_left(positions, key_hash)
if pos_index == len(positions):
pos_index = 0
return hash_ring[positions[pos_index]]
3.2 智能迁移算法
python
复制
下载
class SmartMigrationManager:
"""智能迁移管理器"""
def __init__(self, consistent_hash):
self.ch = consistent_hash
self.migration_log = []
self.migration_state = {}
def add_node_with_migration(self, new_node_config, migration_batch_size=100):
"""添加节点并执行智能迁移"""
# 1. 记录当前状态
old_state = self._capture_current_state()
# 2. 添加新节点
new_node_id = self.ch.add_node(new_node_config)
# 3. 计算需要迁移的数据
migration_plan = self._calculate_migration_plan(old_state, new_node_id)
# 4. 执行迁移
self._execute_migration(migration_plan, migration_batch_size)
return {
'new_node_id': new_node_id,
'migration_plan': migration_plan,
'affected_keys': sum(len(plan['keys']) for plan in migration_plan.values())
}
def _capture_current_state(self):
"""捕获当前状态"""
return {
'virtual_nodes': self.ch.virtual_nodes.copy(),
'sorted_positions': self.ch.sorted_positions.copy(),
'data_distribution': self._get_current_data_distribution()
}
def _calculate_migration_plan(self, old_state, new_node_id):
"""计算迁移计划"""
migration_plan = {}
# 获取新节点的虚拟节点
new_vnodes = self.ch.real_nodes[new_node_id]['virtual_nodes']
for vnode_position in new_vnodes:
# 找到这个虚拟节点在旧环上的前驱
old_predecessor = self._find_predecessor_in_old_ring(
vnode_position, old_state['sorted_positions'], old_state['virtual_nodes']
)
if old_predecessor:
old_node = old_state['virtual_nodes'][old_predecessor]['real_node']
if old_node not in migration_plan:
migration_plan[old_node] = {
'to_node': new_node_id,
'virtual_nodes': [],
'estimated_keys': 0
}
migration_plan[old_node]['virtual_nodes'].append(vnode_position)
# 估算迁移key数量(根据历史数据)
migration_plan[old_node]['estimated_keys'] += self._estimate_keys_per_vnode()
return migration_plan
def _execute_migration(self, migration_plan, batch_size):
"""执行迁移"""
for source_node, plan in migration_plan.items():
print(f"从 {source_node} 迁移到 {plan['to_node']}")
# 实际项目中,这里会:
# 1. 从源节点读取数据
# 2. 写入目标节点
# 3. 更新路由信息
# 4. 验证数据一致性
self._log_migration({
'source': source_node,
'target': plan['to_node'],
'virtual_nodes': plan['virtual_nodes'],
'status': 'completed',
'timestamp': self._current_timestamp()
})
def rolling_update_migration(self, old_node_id, new_node_config):
"""滚动更新迁移(热升级)"""
migration_steps = [
"1. 添加新节点(作为副本)",
"2. 同步数据到新节点",
"3. 逐步将流量切换到新节点",
"4. 验证新节点运行正常",
"5. 移除旧节点"
]
return {
'strategy': 'rolling_update',
'steps': migration_steps,
'downtime': 'zero (理论上)',
'consistency': '最终一致性'
}
四、工程实现方案
4.1 Redis Cluster风格实现
python
复制
下载
class RedisClusterStyleCH:
"""Redis Cluster风格的一致性哈希"""
def __init__(self, slots=16384):
"""
Redis Cluster使用16384个slot
每个key通过CRC16后取模映射到slot
每个节点负责一部分slot
"""
self.total_slots = slots
self.slot_to_node = {} # slot -> 节点映射
self.node_slots = {} # 节点 -> slot列表
self.virtual_slots_per_node = 100 # 每个节点的虚拟slot数
def add_node_with_slots(self, node_id, slot_ranges=None):
"""添加节点并分配slot"""
if slot_ranges is None:
# 自动分配slot
slot_ranges = self._allocate_slots_for_new_node(node_id)
# 记录节点slot
self.node_slots[node_id] = []
for start, end in slot_ranges:
for slot in range(start, end + 1):
self.slot_to_node[slot] = node_id
self.node_slots[node_id].append(slot)
# 如果需要迁移数据
self._reshard_slots_if_needed()
return slot_ranges
def _allocate_slots_for_new_node(self, node_id):
"""为新节点分配slot"""
# 计算每个节点应该负责的slot数
target_slots_per_node = self.total_slots // (len(self.node_slots) + 1)
# 从现有节点中"窃取"一些slot
slots_to_move = []
for existing_node, slots in self.node_slots.items():
if len(slots) > target_slots_per_node:
# 从该节点移动一些slot到新节点
excess = len(slots) - target_slots_per_node
move_count = min(excess, target_slots_per_node - len(slots_to_move))
if move_count > 0:
slots_to_move.extend(slots[:move_count])
self.node_slots[existing_node] = slots[move_count:]
# 分组连续的slot
return self._group_continuous_slots(sorted(slots_to_move))
def get_node_for_key(self, key):
"""获取key对应的节点"""
slot = self._crc16(key) % self.total_slots
return self.slot_to_node.get(slot)
def _crc16(self, data):
"""CRC16算法(与Redis一致)"""
crc = 0
for byte in data.encode():
crc = ((crc << 8) & 0xffff) ^ self._crc16_table[((crc >> 8) ^ byte) & 0xff]
return crc
def migration_plan_for_resharding(self, source_node, target_node, slot_count):
"""生成数据迁移计划"""
slots = self.node_slots[source_node][:slot_count]
migration_plan = {
'source': source_node,
'target': target_node,
'slots': slots,
'keys_to_migrate': self._get_keys_in_slots(source_node, slots),
'steps': [
"1. 设置目标节点为导入状态",
"2. 设置源节点为导出状态",
"3. 批量迁移key",
"4. 更新slot映射",
"5. 清理状态"
]
}
return migration_plan
4.2 生产级实现
python
复制
下载
class ProductionConsistentHash:
"""生产环境一致性哈希实现"""
def __init__(self, config):
self.config = config
self.ring = self._initialize_ring()
self.monitor = self._setup_monitoring()
self.failover_manager = self._setup_failover()
def _initialize_ring(self):
"""初始化哈希环"""
return {
'virtual_nodes': {}, # 虚拟节点
'real_nodes': {}, # 真实节点
'replication_factor': self.config.get('replication_factor', 3),
'zone_aware': self.config.get('zone_aware', False),
'rack_aware': self.config.get('rack_aware', False)
}
def add_node_with_awareness(self, node_info):
"""添加节点(考虑机架和区域感知)"""
if self.ring['zone_aware']:
# 确保副本分布在不同的可用区
self._ensure_zone_distribution(node_info)
if self.ring['rack_aware']:
# 确保副本分布在不同的机架
self._ensure_rack_distribution(node_info)
# 添加虚拟节点
vnode_positions = self._generate_virtual_nodes_with_awareness(node_info)
# 更新路由表
self._update_routing_table()
# 触发数据迁移
self._trigger_controlled_migration()
return vnode_positions
def _ensure_zone_distribution(self, new_node):
"""确保区域分布"""
zones = self._get_current_zones()
if new_node['zone'] in zones:
# 如果该区域已有节点,调整虚拟节点数量
zone_node_count = self._count_nodes_in_zone(new_node['zone'])
# 减少该区域节点的权重
adjustment_factor = 1.0 / (zone_node_count + 1)
return adjustment_factor
return 1.0 # 新区域,全权重
def intelligent_migration(self, source_node, target_node,
migration_strategy='minimal_disruption'):
"""智能迁移"""
strategies = {
'minimal_disruption': {
'特点': '最小化服务中断',
'步骤': [
'1. 建立复制通道',
'2. 增量同步数据',
'3. 短暂双写',
'4. 切换路由',
'5. 清理旧数据'
]
},
'fast_migration': {
'特点': '最快完成迁移',
'步骤': [
'1. 暂停写入',
'2. 批量迁移',
'3. 恢复服务'
]
},
'rolling_migration': {
'特点': '滚动迁移,无感知',
'步骤': [
'1. 按虚拟节点分批迁移',
'2. 每批迁移后验证',
'3. 全部完成后切换'
]
}
}
strategy = strategies.get(migration_strategy, strategies['minimal_disruption'])
# 执行迁移
migration_result = self._execute_migration_strategy(
source_node, target_node, strategy
)
return migration_result
五、性能优化与监控
5.1 性能优化策略
python
复制
下载
class CHPerformanceOptimizer:
"""一致性哈希性能优化"""
def __init__(self):
self.optimizations = self._collect_optimizations()
def _collect_optimizations(self):
"""收集优化策略"""
return {
"查找优化": {
"二分查找优化": {
"标准实现": "bisect.bisect_left O(log N)",
"优化1": "插值查找(当分布均匀时)",
"优化2": "布隆过滤器预判",
"优化3": "缓存热点key的节点映射"
},
"内存布局优化": {
"数组存储": "使用连续内存数组",
"结构体打包": "减少内存碎片",
"CPU缓存友好": "提高缓存命中率"
}
},
"虚拟节点优化": {
"动态调整虚拟节点数": {
"依据": "节点性能差异、负载情况",
"算法": "根据监控数据动态调整",
"公式": "vnode_count = base * (capacity / avg_capacity)"
},
"虚拟节点预计算": {
"思路": "预计算虚拟节点位置",
"实现": "启动时计算,运行时直接使用",
"优化": "使用内存映射文件"
}
},
"迁移优化": {
"增量迁移": {
"批量大小": "动态调整迁移批次大小",
"流量控制": "迁移时不阻塞正常请求",
"并行迁移": "多节点并行迁移"
},
"零拷贝迁移": {
"共享内存": "节点间共享内存区域",
"RDMA": "使用远程直接内存访问",
"优化效果": "减少CPU和网络开销"
}
}
}
def benchmark_comparison(self):
"""性能基准对比"""
test_cases = [
{
'name': '基础一致性哈希',
'virtual_nodes': 0,
'nodes': 10,
'lookup_time': '1.2μs',
'add_node_migration': '12%',
'memory': '1KB'
},
{
'name': '带虚拟节点(100)',
'virtual_nodes': 100,
'nodes': 10,
'lookup_time': '1.5μs',
'add_node_migration': '9%',
'memory': '100KB'
},
{
'name': '带虚拟节点(1000)',
'virtual_nodes': 1000,
'nodes': 10,
'lookup_time': '2.1μs',
'add_node_migration': '1.2%',
'memory': '1MB'
},
{
'name': '智能虚拟节点',
'virtual_nodes': '动态',
'nodes': 10,
'lookup_time': '1.8μs',
'add_node_migration': '0.8%',
'memory': '500KB'
}
]
return test_cases
篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc
需要全套面试笔记及答案
【点击此处即可/免费获取】
5.2 监控与告警
python
复制
下载
class CHMonitoringSystem:
"""一致性哈希监控系统"""
def __init__(self):
self.metrics = self._define_metrics()
self.alerts = self._define_alerts()
def _define_metrics(self):
"""定义监控指标"""
return {
"分布指标": {
"负载均衡度": {
"计算": "标准差/平均值",
"阈值": "< 0.3(30%)",
"采集频率": "每30秒"
},
"热点检测": {
"计算": "单个节点负载/平均负载",
"阈值": "> 2.0(2倍平均)",
"告警级别": "warning"
}
},
"性能指标": {
"查找延迟": {
"P50": "< 1ms",
"P99": "< 10ms",
"P999": "< 50ms"
},
"迁移性能": {
"迁移速度": "MB/s",
"迁移影响": "请求延迟增加比例",
"完成时间": "迁移总耗时"
}
},
"容量指标": {
"节点容量": {
"内存使用率": "< 80%",
"CPU使用率": "< 70%",
"网络带宽": "< 80%"
},
"虚拟节点分布": {
"虚拟节点数": "监控异常波动",
"位置分布": "检查均匀性"
}
}
}
def collect_metrics(self, ch_system):
"""收集指标"""
metrics = {
'timestamp': self._current_time(),
'node_count': len(ch_system.real_nodes),
'virtual_node_count': len(ch_system.virtual_nodes),
'distribution': self._calculate_distribution_metrics(ch_system),
'performance': self._collect_performance_metrics(),
'migration_status': self._get_migration_status()
}
return metrics
def check_anomalies(self, metrics_history):
"""检查异常"""
anomalies = []
# 检查负载均衡
for metric in metrics_history:
if 'distribution' in metric:
cv = metric['distribution'].get('coefficient_of_variation', 0)
if cv > 0.5: # 变异系数超过50%
anomalies.append({
'type': 'load_imbalance',
'value': cv,
'threshold': 0.5,
'timestamp': metric['timestamp']
})
return anomalies
六、实际应用案例
6.1 分布式缓存系统
python
复制
下载
class DistributedCacheWithCH:
"""基于一致性哈希的分布式缓存"""
def __init__(self, nodes, replication=2):
self.ch = EnhancedConsistentHash(nodes)
self.replication = replication
self.cache_nodes = {}
self.migration_manager = SmartMigrationManager(self.ch)
def get(self, key):
"""获取缓存"""
# 1. 找到主节点
primary_node = self.ch.get_node(key)
# 2. 尝试从主节点读取
value = self._read_from_node(primary_node, key)
if value is not None:
return value
# 3. 从副本节点读取
replica_nodes = self._get_replica_nodes(key)
for node in replica_nodes:
value = self._read_from_node(node, key)
if value is not None:
# 修复缓存(写回主节点)
self._repair_cache(primary_node, key, value)
return value
# 4. 缓存未命中
return None
def set(self, key, value, ttl=3600):
"""设置缓存"""
# 1. 找到主节点和副本节点
primary_node = self.ch.get_node(key)
replica_nodes = self._get_replica_nodes(key)
# 2. 写入主节点和所有副本
all_nodes = [primary_node] + replica_nodes
# 3. 使用Quorum写入
success_count = 0
for node in all_nodes:
if self._write_to_node(node, key, value, ttl):
success_count += 1
# 4. 检查写入成功数
if success_count >= self.replication:
return True
# 5. 处理写入失败
self._handle_write_failure(key, value, all_nodes, success_count)
return False
def add_cache_node(self, new_node):
"""添加缓存节点"""
# 1. 添加节点到一致性哈希
result = self.migration_manager.add_node_with_migration(new_node)
# 2. 迁移缓存数据
migration_plan = result['migration_plan']
for source_node, plan in migration_plan.items():
# 迁移虚拟节点对应的key
for vnode_position in plan['virtual_nodes']:
# 获取该虚拟节点负责的key
keys_to_migrate = self._get_keys_for_vnode(source_node, vnode_position)
# 迁移每个key
for key in keys_to_migrate:
value = self.get(key) # 会从副本读取
if value:
# 写入新节点
self._write_to_node(plan['to_node'], key, value)
# 3. 更新路由表
self._update_client_routing_table()
return result
def _get_replica_nodes(self, key):
"""获取副本节点"""
# 顺时针找到后续的replication-1个节点
replica_nodes = []
key_hash = self.ch.hash_func(str(key)) % (2**32)
positions = sorted(self.ch.virtual_nodes.keys())
import bisect
start_index = bisect.bisect_left(positions, key_hash)
for i in range(1, self.replication):
next_index = (start_index + i) % len(positions)
vnode_position = positions[next_index]
node_id = self.ch.virtual_nodes[vnode_position]['real_node']
if node_id not in replica_nodes:
replica_nodes.append(node_id)
return replica_nodes
6.2 数据库分片路由
python
复制
下载
class DatabaseShardingRouter:
"""数据库分片路由"""
def __init__(self, shards_config):
self.shards = {}
self.ch = EnhancedConsistentHash(virtual_nodes_per_node=200)
# 初始化分片
for shard in shards_config:
self.add_shard(shard)
# 初始化连接池
self.connection_pools = self._init_connection_pools()
def add_shard(self, shard_config):
"""添加分片"""
# 根据分片权重设置虚拟节点数
weight = shard_config.get('weight', 1)
# 添加节点
node_id = self.ch.add_node({
'host': shard_config['host'],
'port': shard_config['port'],
'weight': weight,
'shard_id': shard_config['id']
})
# 记录分片信息
self.shards[node_id] = {
'config': shard_config,
'connections': self._create_connection_pool(shard_config),
'stats': {
'query_count': 0,
'error_count': 0,
'avg_latency': 0
}
}
return node_id
def route_query(self, table, shard_key, query_type, query_params):
"""路由查询"""
# 1. 计算分片
if isinstance(shard_key, (list, tuple)):
# 范围查询,可能涉及多个分片
return self._route_range_query(table, shard_key, query_type, query_params)
else:
# 单key查询
shard_id = self._get_shard_for_key(shard_key)
return self._execute_on_shard(shard_id, query_type, query_params)
def _get_shard_for_key(self, key):
"""获取key对应的分片"""
# 使用一致性哈希
node_id = self.ch.get_node(str(key))
if node_id:
# 更新统计
self.shards[node_id]['stats']['query_count'] += 1
return self.shards[node_id]['config']['id']
raise ValueError(f"No shard found for key: {key}")
def rebalance_shards(self, new_shards):
"""重新平衡分片"""
# 1. 计算当前分布
current_distribution = self._analyze_shard_distribution()
# 2. 计算目标分布
target_distribution = self._calculate_target_distribution(new_shards)
# 3. 生成迁移计划
migration_plan = self._generate_migration_plan(
current_distribution, target_distribution
)
# 4. 执行迁移
self._execute_shard_migration(migration_plan)
# 5. 更新路由
self._update_routing_configuration()
return migration_plan
def _generate_migration_plan(self, current, target):
"""生成分片迁移计划"""
plan = {
'total_data_movement': 0,
'estimated_downtime': 0,
'steps': []
}
# 使用最小化数据移动算法
for shard_id, current_data in current.items():
target_data = target.get(shard_id, 0)
if current_data > target_data:
# 需要迁出数据
move_out = current_data - target_data
plan['total_data_movement'] += move_out
plan['steps'].append({
'action': 'move_out',
'shard': shard_id,
'data_amount': move_out,
'target_shards': []
})
return plan
篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc
需要全套面试笔记及答案
【点击此处即可/免费获取】
七、面试要点总结
7.1 核心问题回答模板
python
复制
下载
class CHInterviewGuide:
"""一致性哈希面试指南"""
@staticmethod
def explain_virtual_nodes():
"""解释虚拟节点"""
template = """
虚拟节点机制是一致性哈希的关键优化,主要解决三个问题:
1. 数据倾斜问题:
- 无虚拟节点时,物理节点在环上分布可能不均匀
- 导致某些节点负载过重,某些节点空闲
- 虚拟节点通过多个哈希点使分布更均匀
2. 热点问题:
- 某些数据访问频繁,所在节点成为瓶颈
- 虚拟节点可以将热点数据分散到多个物理节点
- 通过增加虚拟节点数可以平衡热点负载
3. 权重支持:
- 不同节点硬件配置不同
- 通过为高性能节点分配更多虚拟节点
- 使其承担更多负载,实现加权负载均衡
虚拟节点数选择:
- 太少:负载不均
- 太多:内存开销大,查找稍慢
- 经验值:100-1000个/物理节点
"""
return template
@staticmethod
def explain_data_migration():
"""解释数据迁移"""
template = """
数据迁移机制:
1. 添加节点时:
- 新节点在环上占据一段区间
- 原本属于后继节点的部分数据需要迁移到新节点
- 迁移比例 ≈ 1/(N+1),N为原节点数
- 例如:3节点加1节点,约25%数据需要迁移
2. 删除节点时:
- 被删除节点的所有数据需要迁移到后继节点
- 迁移比例 = 该节点原有数据比例
3. 优化策略:
- 增量迁移:分批次迁移,避免服务中断
- 双写策略:迁移期间同时写入新旧节点
- 并行迁移:多个数据段并行迁移
4. 一致性保证:
- 迁移期间需要保证数据一致性
- 可以使用版本号、锁或事务机制
- 分布式环境下需考虑最终一致性
"""
return template
@staticmethod
def system_design_questions():
"""系统设计问题"""
questions = {
"设计题1:设计分布式缓存系统": {
"考察点": [
"一致性哈希的应用",
"虚拟节点机制",
"数据迁移策略",
"高可用设计",
"一致性保证"
],
"回答要点": [
"使用一致性哈希进行数据分片",
"采用虚拟节点解决负载均衡",
"设计智能迁移减少服务中断",
"实现副本机制保证高可用",
"使用Quorum保证一致性"
]
},
"设计题2:设计数据库分片方案": {
"考察点": [
"分片策略选择",
"路由机制",
"扩容方案",
"跨分片查询"
],
"回答要点": [
"基于一致性哈希的自动分片",
"客户端/代理层路由",
"在线扩容和数据迁移",
"合并多个分片查询结果"
]
}
}
return questions
@staticmethod
def coding_questions():
"""编程问题"""
return {
"实现带虚拟节点的一致性哈希": {
"要求": "实现add_node、remove_node、get_node方法",
"考察点": "数据结构、二分查找、环形处理",
"提示": "使用bisect模块,注意环形边界"
},
"实现数据迁移模拟": {
"要求": "模拟添加节点时的数据迁移",
"考察点": "迁移算法、性能优化",
"提示": "计算受影响的数据,分批次迁移"
},
"负载均衡分析": {
"要求": "分析不同虚拟节点数的负载均衡效果",
"考察点": "统计分析、系统评估",
"提示": "计算标准差、变异系数等指标"
}
}
7.2 实战代码示例
python
复制
下载
class CHInterviewCoding:
"""面试编程示例"""
@staticmethod
def implement_consistent_hash():
"""实现一致性哈希(可运行示例)"""
code = """
import bisect
import hashlib
class ConsistentHash:
def __init__(self, virtual_nodes=100):
self.virtual_nodes = virtual_nodes
self.ring = {} # position -> node
self.sorted_positions = []
self.node_virtual_count = {} # node -> virtual count
def _hash(self, key):
return int(hashlib.md5(key.encode()).hexdigest()[:8], 16)
def add_node(self, node):
# 为节点创建虚拟节点
for i in range(self.virtual_nodes):
vnode_key = f"{node}#vnode{i}"
position = self._hash(vnode_key)
# 避免冲突
while position in self.ring:
position = (position + 1) % (2**32)
self.ring[position] = node
self.sorted_positions.append(position)
# 记录节点虚拟节点数
self.node_virtual_count[node] = self.virtual_nodes
# 重新排序
self.sorted_positions.sort()
def remove_node(self, node):
# 移除节点的所有虚拟节点
to_remove = []
for position, n in self.ring.items():
if n == node:
to_remove.append(position)
for position in to_remove:
del self.ring[position]
self.sorted_positions.remove(position)
del self.node_virtual_count[node]
def get_node(self, key):
if not self.ring:
return None
key_hash = self._hash(key)
# 二分查找
idx = bisect.bisect_left(self.sorted_positions, key_hash)
# 环形处理
if idx == len(self.sorted_positions):
idx = 0
return self.ring[self.sorted_positions[idx]]
def analyze_load(self, test_keys=10000):
from collections import Counter
counter = Counter()
for i in range(test_keys):
key = f"test_key_{i}"
node = self.get_node(key)
counter[node] += 1
return dict(counter)
"""
return code
这个一致性哈希的虚拟节点和数据迁移机制详解,涵盖了从基础原理到生产实践的完整内容,特别适合面试准备和实际系统设计。