我将深入解析 MySQL 内核中的核心算法,涵盖索引、查询、事务、恢复等关键系统。这些算法是MySQL高性能和高可靠性的基石。
一、索引与存储算法
1. B+树索引算法
数据结构实现
scss
// storage/innobase/include/btr0btr.h
// B+树节点结构
struct btr_node_t {
page_t* page; // 内存页面
ulint level; // 节点层级(0=叶子)
ulint n_recs; // 记录数
rec_t** recs; // 记录指针数组
btr_node_t** children; // 子节点指针(非叶子节点)
btr_node_t* prev; // 前驱叶子节点
btr_node_t* next; // 后继叶子节点(双向链表)
};
// 插入算法
dberr_t btr_insert(btr_node_t* root, const dtuple_t* entry) {
// 1. 搜索插入位置
btr_path_t* path = btr_search_path(root, entry);
// 2. 叶子节点插入
btr_insert_into_leaf(path->leaf, entry);
// 3. 检查节点分裂条件
if (path->leaf->n_recs > FANOUT) {
// 分裂算法
btr_split_leaf(path->leaf, entry);
// 递归向上分裂
btr_split_non_leaf(path);
}
// 4. 更新父节点指针
btr_update_parent_pointers();
}
关键操作算法
ini
// 1. 搜索算法 - 二分查找
rec_t* btr_search(btr_node_t* node, const dtuple_t* key) {
ulint low = 0, high = node->n_recs - 1;
while (low <= high) {
ulint mid = (low + high) / 2;
int cmp = cmp_dtuple_rec(key, node->recs[mid]);
if (cmp == 0) return node->recs[mid];
else if (cmp < 0) high = mid - 1;
else low = mid + 1;
}
return NULL; // 未找到
}
// 2. 分裂算法
void btr_split_leaf(btr_node_t* leaf, const dtuple_t* new_entry) {
// 计算分裂点(保持大致平衡)
ulint split_point = leaf->n_recs / 2;
// 创建新节点
btr_node_t* new_leaf = btr_create_node();
// 移动后半部分记录到新节点
for (ulint i = split_point; i < leaf->n_recs; i++) {
btr_move_rec_to_node(leaf->recs[i], new_leaf);
}
// 更新双向链表指针
new_leaf->next = leaf->next;
new_leaf->prev = leaf;
if (leaf->next) leaf->next->prev = new_leaf;
leaf->next = new_leaf;
// 更新父节点
btr_insert_into_parent(leaf, new_leaf->recs[0], new_leaf);
}
2. 自适应哈希索引算法
ini
// storage/innobase/include/ha0ha.h
// 哈希函数 - MurmurHash3
ulint hash_calc_hash(const void* data, ulint len) {
const uint32_t c1 = 0xcc9e2d51;
const uint32_t c2 = 0x1b873593;
uint32_t h1 = 0x12345678; // 种子
// 处理完整32位块
for (ulint i = 0; i < len/4; i++) {
uint32_t k1 = ((uint32_t*)data)[i];
k1 *= c1;
k1 = (k1 << 15) | (k1 >> 17);
k1 *= c2;
h1 ^= k1;
h1 = (h1 << 13) | (h1 >> 19);
h1 = h1 * 5 + 0xe6546b64;
}
return h1;
}
// 自适应哈希索引管理
class AdaptiveHashIndex {
hash_table_t* table;
// 自动构建:监控热点访问模式
void monitor_and_build(QueryPattern* pattern) {
if (pattern->frequency > THRESHOLD &&
pattern->selectivity < MAX_SELECTIVITY) {
// 热点查询,自动创建哈希索引
build_hash_index(pattern->columns);
}
}
// 开放地址法解决冲突
ulint find_slot(ulint hash, const void* key) {
ulint idx = hash % table->size;
ulint i = 0;
while (table->slots[idx].occupied &&
!cmp_keys(table->slots[idx].key, key)) {
i++;
idx = (hash + i*i) % table->size; // 二次探测
if (i > MAX_PROBES) return ULINT_UNDEFINED;
}
return idx;
}
};
二、查询处理算法
1. 查询优化器算法
动态规划连接顺序优化
ini
// sql/sql_select.cc
class OptimizerDP {
// 动态规划表
struct DPTableEntry {
table_map tables; // 表集合的位图表示
double cost; // 最小代价
JoinPlan* best_plan; // 最优计划
table_map left; // 左子树
table_map right; // 右子树
JOIN* join; // 连接类型
};
// 动态规划主算法
JoinPlan* find_best_join_order(table_map all_tables) {
DPTableEntry dp[1 << MAX_TABLES];
// 初始化:单表访问代价
for (table_map single_table = 1;
single_table < (1 << all_tables);
single_table <<= 1) {
dp[single_table].cost = calculate_single_table_cost(single_table);
}
// 动态规划:从小到大合并表集合
for (int size = 2; size <= table_count; size++) {
for (table_map S = 1; S < (1 << table_count); S++) {
if (count_bits(S) != size) continue;
// 尝试所有划分 S = L ∪ R
for (table_map L = (S-1) & S; L != 0; L = (L-1) & S) {
table_map R = S & ~L;
double cost = dp[L].cost + dp[R].cost +
calculate_join_cost(L, R);
if (cost < dp[S].cost) {
dp[S].cost = cost;
dp[S].best_plan = combine_plans(dp[L].best_plan,
dp[R].best_plan);
}
}
}
}
return dp[all_tables].best_plan;
}
// 位图操作辅助函数
int count_bits(table_map bits) {
int count = 0;
while (bits) {
bits &= bits - 1;
count++;
}
return count;
}
};
基数估计算法
arduino
// 使用直方图进行选择性估算
class HistogramEstimator {
struct HistogramBucket {
double min_val, max_val;
ulint count; // 该桶中的行数
ulint distinct; // 不同值数量
};
// 等深直方图
vector<HistogramBucket> equi_depth_histogram;
// 估算等值查询的选择性
double estimate_eq_selectivity(double value) {
// 找到值所在的桶
for (auto& bucket : equi_depth_histogram) {
if (value >= bucket.min_val && value <= bucket.max_val) {
// 假设均匀分布
return (double)bucket.count / bucket.total_rows / bucket.distinct;
}
}
return DEFAULT_SELECTIVITY; // 默认值
}
// 估算范围查询的选择性
double estimate_range_selectivity(double low, double high) {
double selectivity = 0.0;
for (auto& bucket : equi_depth_histogram) {
double overlap_ratio = calculate_overlap(bucket, low, high);
selectivity += overlap_ratio * bucket.count / bucket.total_rows;
}
return selectivity;
}
};
2. 连接算法实现
ini
// 哈希连接算法
class HashJoin {
// 构建阶段
void build_phase(Table* build_table, JoinCondition* cond) {
// 1. 创建哈希表
HashTable* ht = create_hash_table(cond->build_key_size);
// 2. 扫描构建表,插入哈希表
for (Row* row = build_table->first_row();
row != NULL;
row = row->next()) {
// 提取连接键
void* key = extract_key(row, cond->build_key_cols);
ulint hash = hash_function(key, cond->build_key_size);
// 插入哈希表(处理冲突)
HashEntry* entry = hash_table_insert(ht, hash, key, row);
}
}
// 探测阶段
ResultSet* probe_phase(Table* probe_table, HashTable* ht, JoinCondition* cond) {
ResultSet* result = create_result_set();
for (Row* probe_row = probe_table->first_row();
probe_row != NULL;
probe_row = probe_row->next()) {
// 提取探测键
void* key = extract_key(probe_row, cond->probe_key_cols);
ulint hash = hash_function(key, cond->probe_key_size);
// 在哈希表中查找匹配
HashEntry* entry = hash_table_lookup(ht, hash, key);
while (entry != NULL) {
if (compare_keys(entry->key, key) == 0) {
// 找到匹配,输出连接结果
Row* joined_row = join_rows(probe_row, entry->row);
result_set_add(result, joined_row);
}
entry = entry->next; // 处理哈希冲突链
}
}
return result;
}
};
三、事务与并发控制算法
1. 多版本并发控制算法
arduino
// storage/innobase/include/read0read.h
// ReadView结构 - 定义事务快照
struct ReadView {
trx_id_t low_limit_id; // 高水位:大于等于此ID的事务不可见
trx_id_t up_limit_id; // 低水位:小于此ID的事务一定可见
ids_t* ids; // 活跃事务ID数组
ulint n_ids; // 活跃事务数量
// 可见性判断算法
bool changes_visible(trx_id_t trx_id, const table_name_t* name) {
// 规则1:创建本ReadView的事务自身修改可见
if (trx_id == creator_trx_id) return true;
// 规则2:事务ID小于低水位一定可见
if (trx_id < up_limit_id) return true;
// 规则3:事务ID大于等于高水位一定不可见
if (trx_id >= low_limit_id) return false;
// 规则4:事务ID在高低水位之间,检查是否活跃
return !is_active(trx_id);
}
// 二分查找判断事务是否活跃
bool is_active(trx_id_t trx_id) {
if (n_ids == 0) return false;
// 活跃事务数组已排序,使用二分查找
ulint i = 0, j = n_ids;
while (i < j) {
ulint mid = (i + j) / 2;
if (ids[mid] == trx_id) return true;
else if (ids[mid] < trx_id) i = mid + 1;
else j = mid;
}
return false;
}
};
2. 两阶段锁算法
scss
// storage/innobase/lock/lock0lock.cc
// 2PL管理器
class TwoPhaseLocking {
// 加锁阶段
LockResult lock_acquire(Transaction* trx, LockRequest* req) {
// 检查锁兼容性
if (!check_lock_compatibility(req)) {
// 不兼容,进入等待
trx->lock_wait.start();
return LOCK_WAIT;
}
// 获取锁
Lock* lock = create_lock(trx, req);
trx->lock_list.push_back(lock);
return LOCK_GRANTED;
}
// 解锁阶段(只在提交或回滚时释放锁)
void lock_release_all(Transaction* trx) {
// 释放所有锁
for (Lock* lock : trx->lock_list) {
remove_lock(lock);
// 唤醒等待该锁的事务
wakeup_waiting_transactions(lock);
}
trx->lock_list.clear();
}
// 死锁检测算法 - 等待图深度优先搜索
bool deadlock_detect(Transaction* start_trx) {
visited_set.clear();
recursion_stack.clear();
return dfs_deadlock_detect(start_trx, 0);
}
bool dfs_deadlock_detect(Transaction* trx, int depth) {
if (recursion_stack.contains(trx)) {
// 找到环
return true;
}
if (visited_set.contains(trx)) {
return false;
}
visited_set.insert(trx);
recursion_stack.push_back(trx);
// 检查trx等待的所有事务
for (Lock* wait_lock : trx->waiting_for) {
Transaction* owner = wait_lock->owner;
if (dfs_deadlock_detect(owner, depth + 1)) {
return true;
}
}
recursion_stack.pop_back();
return false;
}
};
四、恢复算法
1. ARIES恢复算法(MySQL简化实现)
scss
// storage/innobase/log/log0recv.cc
// 三阶段恢复算法
class ARIES_Recovery {
// 阶段1:分析阶段 - 确定脏页和活跃事务
void analysis_pass(LogSequenceNumber checkpoint_lsn) {
// 从检查点开始扫描日志
LogRecord* log = find_log_record(checkpoint_lsn);
while (log != NULL) {
switch (log->type) {
case LOG_UPDATE:
// 记录脏页
dirty_pages.insert(log->page_id);
// 记录活跃事务
active_transactions.insert(log->trx_id);
break;
case LOG_COMMIT:
// 事务提交,从活跃事务移除
active_transactions.erase(log->trx_id);
break;
case LOG_ROLLBACK:
// 事务回滚
active_transactions.erase(log->trx_id);
break;
}
log = log->next;
}
}
// 阶段2:重做阶段 - 重放所有已提交的修改
void redo_pass(LogSequenceNumber start_lsn) {
LogRecord* log = find_log_record(start_lsn);
while (log != NULL) {
if (log->type == LOG_UPDATE) {
// 检查是否需要重做:LSN > PageLSN
PageLSN page_lsn = get_page_lsn(log->page_id);
if (log->lsn > page_lsn) {
// 重做修改
apply_log_to_page(log);
set_page_lsn(log->page_id, log->lsn);
}
}
log = log->next;
}
}
// 阶段3:撤销阶段 - 回滚未完成的事务
void undo_pass() {
// 对每个活跃事务,从后往前回滚
for (trx_id_t trx_id : active_transactions) {
// 获取事务的最后一个日志记录
LogRecord* log = get_last_log_of_transaction(trx_id);
while (log != NULL && log->trx_id == trx_id) {
if (log->type == LOG_UPDATE) {
// 生成补偿日志记录
LogRecord* clr = create_compensation_log(log);
write_log(clr);
// 执行回滚操作
undo_log_operation(log);
}
// 继续前一个日志记录
log = get_prev_log_record(log);
}
}
}
};
五、缓冲池管理算法
1. 改进的LRU-K算法
scss
// storage/innobase/buf/buf0lru.h
class LRU_K_Manager {
// 访问历史记录
struct AccessHistory {
ulint page_id;
ulint access_times[K]; // 最近K次访问的时间戳
ulint last_access_idx; // 上次访问在数组中的索引
};
// LRU列表(分为young和old区域)
list_t* young_list; // 热数据
list_t* old_list; // 冷数据
list_t* free_list; // 空闲页
// 页面访问处理
void page_accessed(ulint page_id) {
AccessHistory* history = get_access_history(page_id);
// 更新访问历史
ulint now = get_current_tick();
history->access_times[history->last_access_idx] = now;
history->last_access_idx = (history->last_access_idx + 1) % K;
// 计算最近K次访问的平均间隔
ulint avg_interval = calculate_average_interval(history);
// 根据访问频率决定页面位置
if (avg_interval < HOT_THRESHOLD) {
// 热点页面,移动到young区域头部
move_to_young_head(page_id);
} else if (avg_interval > COLD_THRESHOLD) {
// 冷页面,移动到old区域尾部
move_to_old_tail(page_id);
}
}
// 页面替换算法
buf_page_t* get_victim() {
// 优先从old区域尾部选择受害者
buf_page_t* victim = get_from_old_tail();
if (victim == NULL) {
// 如果old区域为空,从young区域尾部选择
victim = get_from_young_tail();
}
// 检查页面是否为脏页
if (victim->is_dirty) {
// 启动异步刷脏
flush_page_async(victim);
// 寻找下一个干净页
victim = get_next_clean_victim();
}
return victim;
}
};
六、排序算法
1. 外部排序(Filesort)算法
scss
// sql/filesort.cc
class ExternalSorter {
// 多路归并排序
ResultSet* external_sort(Table* table, SortField* sort_fields, ulint mem_size) {
// 阶段1:生成初始有序段
vector<Run*> runs = create_initial_runs(table, mem_size);
// 阶段2:多路归并
while (runs.size() > 1) {
// 每次合并MERGE_FACTOR个段
vector<Run*> merged_runs;
for (ulint i = 0; i < runs.size(); i += MERGE_FACTOR) {
ulint end = min(i + MERGE_FACTOR, runs.size());
vector<Run*> to_merge(runs.begin() + i, runs.begin() + end);
Run* merged = merge_runs(to_merge, mem_size);
merged_runs.push_back(merged);
}
runs = merged_runs;
}
return convert_to_result_set(runs[0]);
}
// 创建初始有序段
vector<Run*> create_initial_runs(Table* table, ulint mem_size) {
vector<Run*> runs;
Buffer* buffer = allocate_buffer(mem_size);
while (!table->eof()) {
// 读取内存可容纳的数据
ulint rows_read = 0;
while (rows_read < buffer->capacity && !table->eof()) {
Row* row = table->read_next_row();
buffer->add_row(row);
rows_read++;
}
// 内存排序
quick_sort_in_memory(buffer);
// 写入临时文件
Run* run = write_buffer_to_temp_file(buffer);
runs.push_back(run);
buffer->clear();
}
return runs;
}
// 多路归并
Run* merge_runs(vector<Run*> runs, ulint mem_size) {
// 为每个run创建输入缓冲区
vector<Buffer*> input_buffers;
for (Run* run : runs) {
input_buffers.push_back(allocate_input_buffer(run, mem_size / runs.size()));
}
// 输出缓冲区
Buffer* output_buffer = allocate_buffer(mem_size / 2);
Run* merged_run = create_temp_run();
// 使用优先队列(最小堆)进行K路归并
PriorityQueue* pq = create_priority_queue(compare_rows);
// 初始化优先队列
for (ulint i = 0; i < runs.size(); i++) {
if (!input_buffers[i]->empty()) {
Row* first_row = input_buffers[i]->peek();
pq->push(MergeElement{first_row, i});
}
}
// 归并主循环
while (!pq->empty()) {
MergeElement elem = pq->top();
pq->pop();
// 输出当前最小元素
output_buffer->add_row(elem.row);
// 检查输出缓冲区是否已满
if (output_buffer->full()) {
write_buffer_to_run(output_buffer, merged_run);
output_buffer->clear();
}
// 从相应的run中读取下一行
ulint run_idx = elem.run_index;
if (!input_buffers[run_idx]->empty()) {
Row* next_row = input_buffers[run_idx]->read_next();
pq->push(MergeElement{next_row, run_idx});
}
}
// 写出剩余的缓冲区内容
if (!output_buffer->empty()) {
write_buffer_to_run(output_buffer, merged_run);
}
return merged_run;
}
};
七、高级优化算法
1. 遗传算法优化连接顺序
ini
// sql/sql_planner.cc
class GeneticAlgorithmOptimizer {
// 种群个体
struct Individual {
vector<table_map> join_order; // 连接顺序
double cost; // 代价
double fitness; // 适应度
};
// 遗传算法主循环
JoinPlan* genetic_optimize(table_map all_tables) {
// 1. 初始化种群
vector<Individual> population = initialize_population(all_tables, POP_SIZE);
for (int generation = 0; generation < MAX_GENERATIONS; generation++) {
// 2. 评估适应度
evaluate_fitness(population);
// 3. 选择
vector<Individual> parents = tournament_selection(population);
// 4. 交叉
vector<Individual> offspring = crossover(parents);
// 5. 变异
mutate(offspring, MUTATION_RATE);
// 6. 新一代
population = create_new_generation(population, offspring);
}
// 返回最优个体
return get_best_individual(population)->join_order;
}
// PMX(部分映射交叉)算法
vector<Individual> pmx_crossover(Individual& parent1, Individual& parent2) {
Individual child1, child2;
int len = parent1.join_order.size();
// 随机选择两个交叉点
int point1 = random_int(0, len-1);
int point2 = random_int(point1+1, len);
// 初始化子代
child1.join_order.resize(len, -1);
child2.join_order.resize(len, -1);
// 复制中间段
for (int i = point1; i < point2; i++) {
child1.join_order[i] = parent2.join_order[i];
child2.join_order[i] = parent1.join_order[i];
}
// 建立映射关系
map<table_map, table_map> mapping1, mapping2;
for (int i = point1; i < point2; i++) {
mapping1[parent1.join_order[i]] = parent2.join_order[i];
mapping2[parent2.join_order[i]] = parent1.join_order[i];
}
// 填充剩余位置
for (int i = 0; i < len; i++) {
if (i < point1 || i >= point2) {
table_map gene1 = parent1.join_order[i];
table_map gene2 = parent2.join_order[i];
// 处理冲突
while (mapping1.find(gene1) != mapping1.end()) {
gene1 = mapping1[gene1];
}
while (mapping2.find(gene2) != mapping2.end()) {
gene2 = mapping2[gene2];
}
child1.join_order[i] = gene1;
child2.join_order[i] = gene2;
}
}
return {child1, child2};
}
};
八、算法复杂度分析
| 算法 | 平均复杂度 | 最坏情况 | 空间复杂度 | 备注 |
|---|---|---|---|---|
| B+树查找 | O(logₘn) | O(logₘn) | O(n) | m为B+树分支因子 |
| 哈希连接 | O(n+m) | O(n*m) | O(min(n,m)) | 假设哈希表无冲突 |
| 归并连接 | O(n log n + m log m) | O(n log n + m log m) | O(n+m) | 需要排序 |
| 动态规划连接 | O(3ⁿ) | O(3ⁿ) | O(2ⁿ) | n为表数量 |
| 快速排序 | O(n log n) | O(n²) | O(log n) | 内存排序 |
| 外部排序 | O(n logₘn) | O(n logₘn) | O(B) | B为缓冲区大小 |
| 死锁检测 | O(V+E) | O(V+E) | O(V) | 等待图的大小 |
九、实际优化示例
索引条件下推优化
scss
// sql/sql_executor.cc
bool push_index_condition(Item* cond, KEY* key) {
// 分析条件是否可以下推到存储引擎
if (is_index_condition_pushable(cond, key)) {
// 提取下推条件
Item* pushed_cond = extract_pushable_condition(cond, key);
// 修改查询计划,在存储引擎层过滤
query_plan->index_cond = pushed_cond;
// 更新上层过滤条件
remove_pushed_condition_from_where(cond, pushed_cond);
return true;
}
return false;
}
这些算法是MySQL高性能的基石,理解它们对于数据库内核开发至关重要。实际优化时需要根据具体场景选择合适的算法变体和参数配置。