1. 一致性哈希算法原理与实现
一致性哈希是分布式系统中常用的负载均衡算法,它通过构建虚拟节点环来解决传统哈希算法在节点增减时导致的映射关系大规模变动问题。与普通哈希取模不同,一致性哈希在节点变化时只会影响相邻节点的数据迁移。
1.1 核心数据结构设计
我们首先定义物理主机和虚拟节点的类结构:
cpp复制class PhysicalHost {
public:
PhysicalHost(string ip, int size) : ip_(ip) {
for(int i=0; i<size; i++) {
virtualList_.emplace_back(ip_+"#"+to_string(i), this);
}
}
//...其他成员函数
private:
string ip_;
list<VirtualHost> virtualList_;
};
class VirtualHost {
public:
VirtualHost(string ip, PhysicalHost* physicalHost)
: ip_(ip), physicalHost_(physicalHost) {
md5_ = md5::MD5::hash32(ip);
}
//...运算符重载和访问函数
private:
string ip_;
unsigned int md5_;
PhysicalHost* physicalHost_;
};
关键点在于:
- 每个物理主机创建多个虚拟节点
- 虚拟节点通过MD5哈希生成32位标识
- 使用STL set容器自动维护有序的哈希环
1.2 哈希环操作实现
一致性哈希的核心操作包括添加节点、删除节点和查找节点:
cpp复制class ConsistentHash {
public:
void AddVirtualHost(PhysicalHost &host) {
for(auto& virhost : host.GetVirtualList()) {
hashCircle_.insert(virhost);
}
}
void DelVirtualHost(PhysicalHost &host) {
for(auto& virhost : host.GetVirtualList()) {
hashCircle_.erase(virhost);
}
}
string GetPhysicalHostIp(string &client) {
unsigned int md5 = md5::MD5::hash32(client);
for(auto& virhost : hashCircle_) {
if(virhost.Getmd5() > md5) {
return virhost.Getphysicalhost()->GetIp();
}
}
return hashCircle_.begin()->Getphysicalhost()->GetIp();
}
private:
set<VirtualHost> hashCircle_;
};
1.3 MD5哈希实现要点
我们使用标准的MD5算法生成32位哈希值:
cpp复制namespace md5 {
class MD5 {
public:
static uint32_t hash32(const std::string& message) {
MD5 md5;
md5.update(message);
return md5.finalize32();
}
//...其他实现细节
};
}
2. BST二叉搜索树实现
2.1 基础BST结构
我们使用模板类实现BST,支持自定义比较函数:
cpp复制template <typename T, typename Comp = less<T>>
class BSTree {
public:
struct Node {
T data_;
Node* left_;
Node* right_;
Node(T data=0) : data_(data), left_(nullptr), right_(nullptr) {}
};
//...其他成员函数
private:
Node* root_;
Comp comp_;
};
2.2 关键操作实现
2.2.1 插入操作
非递归实现版本:
cpp复制void Insert(T val) {
if(root_ == nullptr) {
root_ = new Node(val);
return;
}
Node* parent = nullptr;
Node* cur = root_;
while(cur != nullptr) {
if(cur->data_ == val) return;
parent = cur;
cur = comp_(cur->data_, val) ? cur->right_ : cur->left_;
}
cur = new Node(val);
comp_(parent->data_, val) ? parent->right_ = cur
: parent->left_ = cur;
}
2.2.2 删除操作
处理三种情况的删除:
cpp复制void erase(T val) {
//...查找要删除的节点
// 情况3:有两个子节点
if(cur->left_ && cur->right_) {
parent = cur;
Node* pre = cur->left_;
while(pre->right_) {
parent = pre;
pre = pre->right_;
}
cur->data_ = pre->data_;
cur = pre;
}
// 情况1和2:0或1个子节点
Node* child = cur->left_ ? cur->left_ : cur->right_;
if(!parent) root_ = child;
else (cur == parent->right_) ? parent->right_ = child
: parent->left_ = child;
delete cur;
}
2.3 遍历算法
2.3.1 非递归中序遍历
cpp复制void n_inorder() {
stack<Node*> s;
Node* cur = root_;
while(!s.empty() || cur) {
if(cur) {
s.push(cur);
cur = cur->left_;
} else {
cur = s.top(); s.pop();
cout << cur->data_ << " ";
cur = cur->right_;
}
}
}
2.3.2 层序遍历
cpp复制void n_levelorder() {
if(!root_) return;
queue<Node*> q;
q.push(root_);
while(!q.empty()) {
Node* front = q.front(); q.pop();
cout << front->data_ << " ";
if(front->left_) q.push(front->left_);
if(front->right_) q.push(front->right_);
}
}
3. 高级BST算法问题
3.1 区间查找
cpp复制void findValues(Node* node, vector<T>& vec, int i, int j) {
if(!node) return;
if(node->data_ >= i)
findValues(node->left_, vec, i, j);
if(node->data_ >= i && node->data_ <= j)
vec.push_back(node->data_);
if(node->data_ <= j)
findValues(node->right_, vec, i, j);
}
3.2 LCA最近公共祖先
cpp复制Node* getLCA(Node* node, int val1, int val2) {
if(!node) return nullptr;
if(comp_(val1, node->data_) && comp_(val2, node->data_))
return getLCA(node->left_, val1, val2);
else if(comp_(node->data_, val1) && comp_(node->data_, val2))
return getLCA(node->right_, val1, val2);
else
return node;
}
3.3 镜像操作
3.3.1 镜像翻转
cpp复制void mirror(Node* node) {
if(!node) return;
swap(node->left_, node->right_);
mirror(node->left_);
mirror(node->right_);
}
3.3.2 镜像对称判断
cpp复制bool mirror_symmetry(Node* node1, Node* node2) {
if(!node1 && !node2) return true;
if(!node1 || !node2) return false;
return node1->data_ == node2->data_ &&
mirror_symmetry(node1->left_, node2->right_) &&
mirror_symmetry(node1->right_, node2->left_);
}
4. 实现中的关键注意事项
-
虚拟节点数量:每个物理主机建议创建150-200个虚拟节点,确保负载均衡
-
哈希冲突处理:当两个虚拟节点哈希值相同时,需要特殊处理(如追加序号)
-
BST删除递归陷阱:递归删除时要注意情况3转换为情况1/2后的处理差异
-
线程安全:实际生产环境中需要添加锁机制保护数据结构
-
内存管理:BST删除节点时要确保正确释放内存,避免内存泄漏
5. 性能优化建议
-
哈希计算优化:可以缓存MD5计算结果,避免重复计算
-
平衡BST:对于频繁更新的BST,可以考虑实现AVL或红黑树保持平衡
-
批量操作:一致性哈希支持批量添加/删除节点,减少锁竞争
-
遍历优化:非递归遍历算法可以显著减少函数调用开销
-
内存池:对于频繁的节点创建删除,可以使用内存池提高性能
