Raft协议
概念
Raft 是一个分布式一致性算法,被设计为比 Paxos 更易于理解,同时具备相似的性能和安全性。它常用于构建容错的分布式系统,确保多个节点在面对网络分区、节点失效等情况下能够达成一致
核心目标
- Leader选举:通过选举机制确保每个时间段只有一个节点(Leader)负责日志复制和状态变更。
- 日志复制:Leader 将客户端的操作(日志)复制到其他节点(Follower),确保日志的一致性。
- 状态机一致性:通过确保所有节点按相同顺序应用日志,实现一致性。
主要模块
1. 角色
- Leader:负责接收客户端请求,将操作以日志形式写入并同步给 Follower。
- Follower:响应 Leader 的同步请求,被动地接受 Leader 的日志和指令。
- Candidate:在 Leader 失效后,由 Follower 转为 Candidate,通过投票选举自己为新 Leader。
2. 选举过程
- 若 Follower 超时未收到 Leader 的心跳信号,会转为 Candidate 并发起选举。
- 每个节点在选举期间投票给自己,同时请求其他节点投票。
- 如果一个 Candidate 获得了超过半数的投票,则成为 Leader。
3. 日志复制
- Leader 接收到客户端请求后,将其作为日志条目添加到自己的日志中。
- Leader 使用 AppendEntries RPC 将日志复制到 Follower。
- 当多数节点确认日志条目后,Leader 将日志提交,并通知所有节点应用日志到状态机。
4. 一致性保证
- 使用 任期号(Term) 防止陈旧的 Leader 发出无效指令。
- 确保日志条目在所有节点上按照相同顺序出现,避免状态不一致。
扩展:MultiRaft协议
MultiRaft 是 Raft 的一种扩展,旨在支持多个 Raft 实例同时运行,以便在大规模分布式系统中更高效地管理数据分片和分布式事务。
动机
- 单个 Raft 实例在处理大量数据时可能成为瓶颈。
- 在分布式系统中,通常需要对数据进行分区,每个分区由独立的一组节点管理。
- MultiRaft 提供了一种机制,通过运行多个 Raft 实例,每个实例负责一部分数据,从而提高系统的吞吐量和扩展性
核心思想
- 多实例并行运行:
- 每个 Raft 实例管理一个独立的数据分片(shard)。
- 每个实例有自己的 Leader、Follower 和日志,独立运行 Raft 协议。
- 共享底层资源:
- 多个 Raft 实例可以运行在相同的物理节点上,共享网络、存储和 CPU 等资源。
- 使用高效的调度机制协调实例间的资源竞争。
- 动态分片和迁移:
- 数据分片可以动态调整,每个分片由一个 Raft 实例管理。
- 分片可以在节点之间迁移,以应对节点故障或负载不均。
- 跨分片操作:
- 支持分布式事务,需要在多个 Raft 实例之间协调。
- 一般通过两阶段提交(2PC)或共识组间通信来实现。
对比
| 特性 | Raft | MultiRaft |
|---|---|---|
| 目标 | 提供单一一致性机制 | 提供分区一致性机制 |
| 运行实例 | 单个 Raft 集群 | 多个独立的 Raft 集群 |
| 适用场景 | 小规模系统 | 大规模分布式存储或事务场景 |
| 扩展性 | 有限,单点可能成为瓶颈 | 高扩展性,分片机制避免瓶颈 |
| 复杂度 | 较低 | 较高,需要处理跨分片事务 |
Buffer Bulk Eviction
If bulk reads or writes are performed, there is a risk that one-time data can quickly oust useful pages from the buffer cache.
As a precaution, bulk operations use rather small buffer rings, and eviction is performed within their boundaries, without affecting other buffers.
A buffer ring of a particular size consists of an array of buffers that are used one after another. At first, the buffer ring is empty, and individual buffers join it one by one, after being selected from the buffer cache in the usual manner. Then eviction comes into play,but only within the ring limits
Buffers added into a ring are not excluded from the buffer cache and can still be used by other operations. So if the buffer to be reused turns out to be pinned, or its usage count is higher than one, it will be simply detached from the ring and replaced by another buffer.
PostgreSQL supports three eviction strategies.
| strategy | trigger | buffer ring |
|---|---|---|
| Bulk reads | sequential scans of large tables if their size exceeds 1/4 of the buffer cache(128MB:16384 page) | 256KB(32 page) |
| Bulk writes | applied by Copy from, create table as select , and create materialized view commands, as well as by those alter table flavors that cause table rewrites. | default: 16MB(2048 page) |
| Vacuuming | full table scan without taking the visibility map into account | 256KB(32 page) |
Buffer rings do not always prevent undesired eviction. If UPDATE or DELETE commands affect a lot of rows, the performed table scan applies the bulk reads strategy, but since the pages are constantly being modified, buffer rings virtually become useless.
Another example worth mentioning is storing oversized data in TOAST tables. In spite of a potentially large volume of data that has to be read, toasted values are always accessed via an index, so they bypass buffer rings.
Let’s take a closer look at the bulk reads strategy. For simplicity, we will create a table in such a way that an inserted row takes the whole page. By default, the buffer cache size is 16,384 pages, 8 kb each. So the table must take more than 4096 pages for the scan to use a buffer ring.
1 | test=# SHOW shared_buffers; |
参考书目
- Egor Rogov, PostgreSQL 14 Internals, https://postgrespro.com/community/books/internals
gdb 查看指令
使用 x 命令和内存地址:
x (examine) 命令允许你检查内存中的数据。你可以指定要检查的内存地址、格式和单位。
基本语法: x /nfu addr
n:要显示的单位数量。
f:显示格式(例如,x 表示十六进制,t 表示二进制,d 表示十进制)。
u:单位大小(b 表示字节,h 表示半字(16 位),w 表示字(32 位),g 表示巨字(64 位))。
addr:要检查的内存地址。
示例: 假设有一个 32 位整数 val,其地址为 0x12345678,你想以 16 位为单位查看:
1 | x /2ht 0x12345678 |
这将显示从 0x12345678 开始的两个半字(16 位)的十六进制值。
结合 p 命令获取地址: 你可以使用 p &变量名 获取变量的地址,然后将其传递给 x 命令。
1 |
|
编译并用 GDB 调试:
1 | gcc -g test.c -o test |
在 GDB 中:
1 | (gdb) break main |
这里注意字节序,在小端序机器上,低位字节在前,高位字节在后,所以显示为 0x5678 0x1234。
如果想要二进制显示,则使用:
1 | x /2ht 0x7fffffffe0dc |
使用表达式和位运算(对于变量值):
如果你只想查看变量的值,而不需要查看内存,可以使用 C 语言的位运算来提取 16 位部分,然后使用 p/t 显示。
示例:
1 | int val = 0x12345678; |
在 GDB 中:
1 | (gdb) p/t (val & 0xFFFF) // 获取低 16 位 |
总结:
对于查看内存中的数据(包括变量在内存中的表示),使用 x 命令结合 h (半字) 和 t (二进制) 格式指定符是最直接的方法。
对于只查看变量的值,使用位运算提取 16 位部分,然后使用 p/t 也是一个有效的选择。
选择哪种方法取决于你的具体需求。如果你需要查看变量在内存中的布局(例如,在结构体或数组中),x 命令是更好的选择。如果你只需要查看变量值的不同部分,位运算可能更方便。
gdb 异步调试
1 | set target-async 1 |
分析tcmalloc 内存增长 —- pprof
有时候程序莫名内存上涨厉害,但不是泄漏,如何定位哪申请使用的大量内存呢?
如果没使用tcmalloc,使用heaptrack工具(git clone https://github.com/KDE/heaptrack.git);
如果使用了tcmalloc,可以通过tcmalloc自带的工具来分析
一 工具
- tcmalloc
- pprof
pprof相当于分析器,阅读器,分析阅读的内容为heap文件,内容包括
- 每个活跃分配的大小
- 分配的调用栈
- 分配次数
二 生成heap文件
手动
在程序的入口或需要开始分析的地方,添加 HeapProfilerStart(“文件路径”)。
在程序结束或需要停止分析的地方,添加 HeapProfilerStop()。
在需要手动生成快照的地方,添加 HeapProfilerDump(“文件名”)。自动
设置环境变量,动态加载设置环境变量即可,tcmalloc库加载时会读取并解析这个环境变量,从而开启堆分析功能1
export HEAP_PROFILE=/tmp/myapp_heap
如果是静态加载,需代码中启动:HeapProfilerStart
补充说明
自动生成文件的时机:申请内存超过阈值时会自动生成,阈值可以通过HEAP_PROFILE_ALLOCATION_INTERVAL环境变量来设置
三 分析heap文件(快照)
单次快照
1 | # 文本格式 |
两次快照:
1 | # 对比两次快照 |
火焰图需要额外安装
1 | yum install graphviz |
sample
1 | #include <gperftools/heap-profiler.h> |
1 | g++ test_heap.cc -ltcmalloc -lpthread -lunwind -o test_heap |
Install postgres from codes in linux
First, we need to create a postgres user and add it to the sudo group
1 | usermod -aG sudo postgres # 注意安全,云主机不要这么设置 |
Get codes
1 | git clone --depth=1 https://git.postgresql.org/git/postgresql.git |
Compile after installing dependencies
1 | sudo apt install -y \ |
Modify environment variables
create a configure file
1 | ~/.pg_env |
1 | # 设置当前使用的 PostgreSQL 安装路径 |
initdb
1 | sudo chown -R postgres:postgres /usr/local/pgsql |
start
1 | pg_ctl -D /usr/local/pgsql/data -l logfile start |
About Extensions
cd contrib
1 | make |
登录你要使用的数据库,然后执行:
1 | CREATE EXTENSION pageinspect; |
Hello World
Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.
Quick Start
Create a new post
1 | $ hexo new "My New Post" |
More info: Writing
Run server
1 | $ hexo server |
More info: Server
about draft
1 | hexo new draft "草稿标题" |
Generate static files
1 | $ hexo generate |
More info: Generating
Deploy to remote sites
1 | $ hexo deploy |
More info: Deployment
git
- 合并提交(本地未push)
1
2
3git status
git add -u
git commit --amend --no-edit - commit 合并
- 方法1: 将你想合并的提交改为 squash 或简写 s,保留第一个 pick 不动:
1
2
3
4
5git rebase -i HEAD~3
pick abc123 Commit message 1
pick def456 Commit message 2
pick 789abc Commit message 3保存退出,Git 会让你编辑最终的 commit message,合并或者修改后保存即可。1
2
3pick abc123 Commit message 1
squash def456 Commit message 2
squash 789abc Commit message 3
强行推送:1
git push origin your-branch --force-with-lease
- 方法2
1
2
3
4
5
6
7
8
9# 回到上一次 push 的状态(或者想作为基准的 commit)
git reset --soft origin/your-branch
# 或者 git reset --soft HEAD~N (如果你想数 N 个提交的话)
# 将所有改动重新提交为一个 commit
git commit -m "合并后的 commit message"
# 推送到远程(已 push 的分支需要强制)
git push --force-with-lease
- 忽略所有名为 build 的目录
编辑.gitignore 文件1
2
3
4
5
6
7
8保留仓库根目录下的 build/README.md
build/
**/build/*.log
//任意层级 build 目录下的 README.md
build/
!**/build/
!**/build/README.md