pgdogdev/pgdog
GitHub: pgdogdev/pgdog
一个用 Rust 编写的 PostgreSQL 代理,集连接池、负载均衡和分片于一体,帮助数据库水平扩展。
Stars: 4062 | Forks: 150
[](https://github.com/levkk/pgdog/actions/workflows/ci.yml)
PgDog 是一个用于扩展 PostgreSQL 的代理。它支持连接池、查询负载均衡和全库分片。PgDog 使用 Rust 编写,快速、安全,可以在普通硬件上管理数千个连接。
## 文档
📘 PgDog 文档**[可以在这里找到](https://docs.pgdog.dev/)**。有任何问题?在 **[Discord](https://discord.com/invite/CcBZkjSJdd)** 上与我们聊天。
## 快速开始
### Kubernetes
Helm chart 在**[这里](https://github.com/pgdogdev/helm)**。要安装它,请运行:
```
helm repo add pgdogdev https://helm.pgdog.dev
helm install pgdog pgdogdev/pgdog
```
### AWS
如果您使用的是 AWS RDS,可以通过以下两种支持的方法之一部署 PgDog:
1. 结合 [EKS](https://aws.amazon.com/eks/) 使用 [Helm chart](https://github.com/pgdogdev/helm),或自托管的 Kubernetes 集群
2. [Terraform module](https://github.com/pgdogdev/pgdog-ecs-terraform) 在 [ECS](https://aws.amazon.com/ecs/) 上部署 PgDog
### 在 Docker 中试用
您可以使用 Docker 快速试用 PgDog。安装 [Docker Compose](https://docs.docker.com/compose/) 并运行:
```
docker-compose up
```
启动后,您可以使用 psql 或任何其他 PostgreSQL 客户端连接到 PgDog:
```
PGPASSWORD=postgres psql -h 127.0.0.1 -p 6432 -U postgres
```
该演示附带 3 个分片和 2 个分片表:
```
INSERT INTO users (id, email) VALUES (1, 'admin@acme.com');
INSERT INTO payments (id, user_id, amount) VALUES (1, 1, 100.0);
SELECT * FROM users WHERE id = 1;
SELECT * FROM payments WHERE user_id = 1;
```
## 功能特性
📘 **[配置](https://docs.pgdog.dev/configuration/)**
所有 PgDog 功能均可配置,并且可以开启或关闭。PgDog 需要 2 个配置文件才能运行:
1. `pgdog.toml`:主机、分片配置和其他设置
2. `users.toml`:用户名和密码
### 示例
大多数选项都有合理的默认值,因此在同一台机器上运行的基本配置
对于单个用户和数据库来说非常简短:
**`pgdog.toml`**
```
[general]
port = 6432
default_pool_size = 10
[[databases]]
name = "pgdog"
host = "127.0.0.1"
```
**`users.toml`**
```
[[users]]
name = "alice"
database = "pgdog"
password = "hunter2"
```
如果 `pgdog.toml` 中的数据库在 `users.toml` 中没有对应的用户,则不会为该数据库创建连接池,用户将无法连接。
如果您想在本地试用,请像这样创建数据库和用户:
```
CREATE DATABASE pgdog;
CREATE USER pgdog PASSWORD 'pgdog' LOGIN;
```
### 事务池
📘 **[事务](https://docs.pgdog.dev/features/transaction-mode)**
像 PgBouncer 一样,PgDog 支持事务(和会话)池,允许数千个客户端仅使用少量的 PostgreSQL 服务器连接。
与 PgBouncer 不同,PgDog 可以解析和处理 `SET` 语句和启动选项,确保在具有不同参数的客户端之间共享服务器连接时,会话状态设置正确。
PgDog 还具有更高级的连接恢复选项,例如自动回滚废弃的事务和连接重新同步,以避免在应用程序崩溃期间频繁更换服务器连接。
### 负载均衡器
📘 **[负载均衡器](https://docs.pgdog.dev/features/load-balancer/)**
PgDog 是一个用于 PostgreSQL 的应用层(OSI 第 7 层)负载均衡器。它理解 Postgres 协议,可以代理多个副本(和主库),并在数据库之间均匀分配事务。负载均衡器支持 3 种策略:轮询、随机和最少活跃连接。
**示例**
当数据库有多个主机时,负载均衡器会自动启用:
```
[[databases]]
name = "prod"
host = "10.0.0.1"
role = "primary"
[[databases]]
name = "prod"
host = "10.0.0.2"
role = "replica"
```
#### 健康检查
📘 **[健康检查](https://docs.pgdog.dev/features/load-balancer/healthchecks/)**
PgDog 维护一个实时的健康主机列表。当数据库未通过健康检查时,它会从活跃轮换中移除,查询将被重新路由到其他副本。这就像 HTTP 负载均衡器一样工作,只不过它是针对您的数据库的。
健康检查可最大限度地提高数据库可用性,并防止糟糕的网络连接、临时硬件故障或配置错误。
#### 单一端点
📘 **[单一端点](https://docs.pgdog.dev/features/load-balancer/#single-endpoint)**
PgDog 使用 [`pg_query`](https://github.com/pganalyze/pg_query.rs),其中包含 PostgreSQL 原生解析器。通过解析查询,PgDog 可以检测写操作(例如 `INSERT`、`UPDATE`、`CREATE TABLE` 等)并将其发送到主库,让副本服务于读操作(`SELECT`)。这允许应用程序连接到同一个 PgDog 部署进行读写操作。
##### 事务
📘 **[负载均衡器与事务](https://docs.pgdog.dev/features/load-balancer/transactions/)**
事务可以执行多条语句,因此在主备配置中,PgDog 将它们路由到主库。客户端可以指示事务是只读的,在这种情况下,PgDog 会将其发送到副本:
```
BEGIN READ ONLY;
-- This goes to a replica.
SELECT * FROM users LIMIT 1;
COMMIT;
```
#### 故障转移
📘 **[故障转移](https://docs.pgdog.dev/features/load-balancer/replication-failover/)**
PgDog 监控 Postgres 复制状态,并且如果副本被提升,可以自动将写操作重定向到不同的数据库。这并不能替代像 Patroni 这样实际编排故障转移的工具。您可以将 PgDog 与 Patroni(或 AWS RDS 或其他托管的 Postgres 主机)一起使用,以优雅地对实时流量进行故障转移。
**示例**
要启用故障转移,请将所有数据库的 `role` 属性设置为 `auto` 并启用复制监控(`lsn_check_delay` 设置):
```
[general]
lsn_check_delay = 0
[[databases]]
name = "prod"
host = "10.0.0.1"
role = "auto"
[[databases]]
name = "prod"
host = "10.0.0.2"
role = "auto"
```
### 认证
📘 **[认证](https://docs.pgdog.dev/features/authentication/)**
PgDog 支持两种认证方法:
1. 基于密码
2. AWS RDS IAM
#### 基于密码的认证
基于密码的认证允许客户端向 PgDog 进行认证,以及 PgDog 向 PostgreSQL 进行认证。目前支持以下密码哈希算法:
- SCRAM-SHA-256
- MD5
- 明文
#### RDS IAM 后端认证
PgDog 可以保持客户端到 PgDog 的认证不变,同时基于每个用户使用 AWS RDS IAM 令牌进行 PgDog 到 PostgreSQL 的认证。
**示例**
```
[[users]]
name = "alice"
database = "pgdog"
password = "client-password"
server_auth = "rds_iam"
# 可选;省略时,PgDog 从 *.region.rds.amazonaws.com(.cn) 主机名推断 region。
# server_iam_region = "us-east-1"
```
当任何用户设置了 `server_auth = "rds_iam"` 时,还必须配置以下设置:
- `tls_verify` **不能**是 `"disabled"`。
- `passthrough_auth` 必须是 `"disabled"`。
### 分片
📘 **[分片](https://docs.pgdog.dev/features/sharding/)**
PgDog 能够管理具有多个分片的数据库。通过使用 PostgreSQL 解析器,PgDog 提取分片键并为每个查询确定最佳路由策略。
对于跨分片查询,PgDog 在内存中组装和转换结果,将所有行发送给客户端,就像它们来自单个数据库一样。
**示例**
为同一个数据库配置具有不同分片号(`shard` 设置)的多个主机将启用分片:
```
[[databases]]
name = "prod"
host = "10.0.0.1"
shard = 0
[[databases]]
name = "prod"
host = "10.0.0.2"
shard = 1
```
注意:请阅读下文了解如何配置查询路由。至少需要一个分片表才能使分片按预期工作。
#### 分片函数
📘 **[分片函数](https://docs.pgdog.dev/features/sharding/sharding-functions/)**
PgDog 有两个主要的分片算法:
1. PostgreSQL 分区函数(`HASH`、`LIST`、`RANGE`)
2. 使用模式
##### 基于分区的分片
基于分区的分片函数直接取自 Postgres 源代码。这种选择特意允许使用 PgDog 和 Postgres [外部表](https://www.postgresql.org/docs/current/sql-createforeigntable.html)以及 [`postgres_fdw`](https://www.postgresql.org/docs/current/postgres-fdw.html) 对数据进行分片。
**示例**
配置分片表时,默认使用 `PARTITION BY HASH` 算法:
```
[[sharded_tables]]
database = "prod"
column = "user_id"
```
基于列表的分片(与 Postgres 中的 `PARTITION BY LIST` 相同)可以配置如下:
```
# 仍需 Sharded table 定义。
[[sharded_tables]]
database = "prod"
column = "user_id"
# 特定 Value 的 shard 映射。
[[sharded_mapping]]
database = "prod"
column = "user_id"
values = [1, 2, 3, 4]
shard = 0
[[sharded_mapping]]
database = "prod"
column = "user_id"
values = [5, 6, 7, 8]
shard = 1
```
对于基于范围的分片,将 `values` 设置替换为范围,例如:
```
start = 0 # include
end = 5 # exclusive
```
##### 基于模式的分片
📘 **[基于模式的分片](https://docs.pgdog.dev/configuration/pgdog.toml/sharded_schemas/)**
基于模式的分片基于 PostgreSQL 模式工作。同一模式下的表放置在同一个分片上,引用这些表的所有查询都会自动路由到该分片。
**示例**
配置分片模式使用与分片表不同的配置:
```
[[sharded_schemas]]
database = "prod"
name = "customer_a"
shard = 0
[[sharded_schemas]]
database = "prod"
name = "customer_b"
shard = 1
```
引用模式 `customer_a` 中表的查询将被发送到分片 0。例如,通过其完全限定名称引用表的查询将仅发送到一个分片:
```
INSERT INTO customer_a.orders (id, user_id, amount)
VALUES ($1, $2, $3);
```
或者,可以在 `search_path` 会话变量中指定模式名称:
```
SET search_path TO public, customer_a;
-- All subsequent queries will be sent to shard 0.
SELECT * FROM orders LIMIT 1;
```
您还可以使用 `SET LOCAL` 在单个事务期间设置 `search_path`,确保仅将该事务发送到所需的分片:
```
-- The entire transaction will be sent to shard 1.
BEGIN;
SET LOCAL search_path TO public, customer_b;
SELECT * FROM orders LIMIT 1;
COMMIT;
```
#### 直达分片查询
📘 **[直达分片查询](https://docs.pgdog.dev/features/sharding/query-routing/)**
包含分片键的查询仅发送到一个数据库。这是分片数据库的最佳情况,因为负载在集群中均匀分布。
**示例**:
```
-- user_id is the sharding key.
SELECT * FROM users WHERE user_id = $1;
```
#### 跨分片查询
- 📘 **[跨分片查询](https://docs.pgdog.dev/features/sharding/cross-shard-queries/)**
- 📘 **[SELECT](https://docs.pgdog.dev/features/sharding/cross-shard-queries/select/)**
- 📘 **[INSERT](https://docs.pgdog.dev/features/sharding/cross-shard-queries/insert/)**
- 📘 **[UPDATE 和 DELETE](https://docs.pgdog.dev/features/sharding/cross-shard-queries/update/)**
- 📘 **[DDL](https://docs.pgdog.dev/features/sharding/cross-shard-queries/ddl/)**
具有多个分片键或没有分片键的查询将发送到所有数据库,并在内存中对结果进行后处理和组装。PgDog 然后将最终结果发送给客户端。
目前,跨分片查询中对某些 SQL 功能的支持是有限的。但是,支持的功能列表正在不断增加:
| 功能 | 支持 | 说明 |
|-|-|-|
| 聚合函数 | 部分 | 支持 `count`、`min`、`max`、`stddev`、`variance`、`sum`、`avg`。 |
| `ORDER BY` | 部分 | `ORDER BY` 子句中的列必须存在于结果集中。 |
| `GROUP BY` | 部分 | 与 `ORDER BY` 相同,引用的列必须存在于结果集中。 |
| 多元组 `INSERT` | 支持 | PgDog 为每个元组生成一条语句并自动执行它们。 |
| 分片键 `UPDATE` | 支持 | PgDog 生成 `SELECT`、`INSERT` 和 `DELETE` 语句并自动执行它们。 |
| 子查询 | 否 | 同一个子查询在所有分片上执行。 |
| CTEs | 否 | 同一个 CTE 在所有分片上执行。 |
#### 使用 `COPY`
📘 **[Copy](https://docs.pgdog.dev/features/sharding/cross-shard-queries/copy/)**
PgDog 具有文本、CSV 和二进制解析器,可以自动将通过 `COPY` 命令发送的行分配到所有分片。这允许客户端将数据摄取到分片的 PostgreSQL 中,而无需预处理。
**示例**
```
COPY orders (id, user_id, amount) FROM STDIN CSV HEADER;
```
必须在 `COPY` 语句中指定列,以便 PgDog 自动推断分片键,但在数据文件中是可选的。
#### 一致性(两阶段提交)
📘 **[两阶段提交](https://docs.pgdog.dev/features/sharding/2pc/)**
为了确保跨分片写入是原子的,PgDog 支持 Postgres 的[两阶段事务](https://www.postgresql.org/docs/current/two-phase.html)。启用后,PgDog 通过代表客户端执行 2pc 交换来处理客户端发送的 `COMMIT` 语句:
```
PREPARE TRANSACTION '__pgdog_unique_id';
COMMIT PREPARED '__pgdog_unique_id';
```
如果客户端断开连接或 Postgres 崩溃,如果处于第一阶段,PgDog 将自动回滚事务,如果处于第二阶段,则提交事务。
#### 唯一标识符
📘 **[唯一 ID](https://docs.pgdog.dev/features/sharding/unique-ids/)**
虽然应用程序可以使用 `UUID`(v4 和现在的 v7)生成唯一主键,但 PgDog 支持创建唯一的 `BIGINT` 标识符,而无需使用序列:
```
SELECT pgdog.unique_id();
```
这使用基于时间戳的算法,每秒可以产生数百万个唯一数字,并且不需要昂贵的跨分片索引来保证唯一性。
#### 分片键更新
PgDog 支持在线更改行的分片键。在底层,它将执行 3 条语句来实现它:
1. `SELECT` 从原始分片获取整行
2. `INSERT` 将新的、更改的行写入新分片
3. `DELETE` 将其从旧分片中删除
这是自动发生的,客户端可以像往常一样检索新行:
```
UPDATE orders SET user_id = 5 WHERE user_id = 1 RETURNING *;
-- This will return the new row
```
注意:一次只能更新一行,如果查询尝试更新多行,PgDog 将中止事务。
要启用分片键更新,请将此添加到 `pgdog.toml`:
```
[rewrite]
enabled = true
shard_key = "rewrite" # options: ignore (possible data loss), error (block shard key update)
```
#### 多元组插入
PgDog 可以通过将每个元组发送到正确的分片来处理多元组 `INSERT` 查询,例如:
```
INSERT INTO payments
(id, user_id, amount) -- user_id is the sharding key
VALUES
(pgdog.unique_id(), 1, 25.00), -- Tuples go to different shards
(pgdog.unique_id(), 5, 55.0); -- Each tuple gets a unique primary key because unique ID function is invoked twice
```
如果启用,这将自动发生:
```
[rewrite]
enabled = true
split_inserts = "rewrite" # other options: ignore, error
```
#### 重新分片
- 📘 **[重新分片](https://docs.pgdog.dev/features/sharding/resharding/)**
- 📘 **[模式同步](https://docs.pgdog.dev/features/sharding/resharding/schema/)**
- 📘 **[数据同步](https://docs.pgdog.dev/features/sharding/resharding/hash/)**
PgDog 理解 PostgreSQL 逻辑复制协议,并且可以在后台且无停机的情况下在数据库之间编排数据拆分。这允许对现有数据库进行分片并在生产环境中向现有集群添加更多分片,而不会影响数据库操作。
重新分片过程分 5 步完成:
1. 创建具有所需分片数的新空集群
2. 在 `pgdog.toml` 中配置它并运行 `schema-sync` 命令以表模式复制到新数据库
3. 运行 `data-sync` 命令以使用逻辑复制复制和重新分片表数据(表并行复制)
4. 在保持前一个命令运行(它实时流式传输行更新)的同时,运行 `schema-sync --data-sync-complete` 在新数据库上创建二级索引(在复制数据后执行此操作要快得多)
5. 使用 `MAINTENANCE ON`、`RELOAD`、`MAINTENANCE OFF` 命令序列将流量切换到新集群
使用多个 PgDog 容器可以原子地完成切换,因为 `RELOAD` 不会恢复流量,`MAINTENANCE OFF` 会,因此在恢复查询之前所有容器中的配置都是相同的。不需要像 etcd 或 Zookeeper 这样复杂的同步工具。
### 监控
📘 **[指标](https://docs.pgdog.dev/features/metrics/)**
PgDog 同时公开标准 PgBouncer 风格的管理数据库和 OpenMetrics 端点。管理数据库并非 100% 兼容,
因此我们建议您使用 OpenMetrics 进行监控。示例 Datadog 配置和仪表板[包含在内](examples/datadog)。
## 本地运行 PgDog
从 [rust-lang.org](https://rust-lang.org) 安装最新版本的 Rust 编译器。
克隆此仓库并以 release 模式构建项目:
```
cargo build --release
```
如果您要部署到生产环境或想要运行性能基准测试,请务必使用 release 配置文件。
#### 尝试分片
分片数据库集群在配置中设置。例如,要设置 2 个分片集群,您可以:
**`pgdog.toml`**
```
[[databases]]
name = "pgdog_sharded"
host = "127.0.0.1"
database_name = "shard_0"
shard = 0
[[databases]]
name = "pgdog_sharded"
host = "127.0.0.1"
database_name = "shard_1"
shard = 1
[[sharded_tables]]
database = "pgdog_sharded"
column = "user_id"
```
不要忘记配置用户:
**`users.toml`**
```
[[users]]
database = "pgdog_sharded"
name = "pgdog"
password = "pgdog"
```
最后,为了使其在本地工作,创建所需的数据库:
```
CREATE DATABASE shard_0;
CREATE DATABASE shard_1;
GRANT ALL ON DATABASE shard_0 TO pgdog;
GRANT ALL ON DATABASE shard_1 TO pgdog;
```
### 启动 PgDog
可以使用 Cargo 运行 PgDog:
```
cargo run --release
```
#### 命令行选项
PgDog 支持几个命令行选项:
- `-c, --config
`:配置文件的路径(默认:`"pgdog.toml"`)
- `-u, --users `:users.toml 文件的路径(默认:`"users.toml"`)
- `-d, --database_url `:连接 URL。可以多次指定以添加多个数据库连接。提供时,这些 URL 将覆盖配置文件中的数据库配置。
直接使用数据库 URL 的示例:
```
cargo run --release -- -d postgres://user:pass@localhost:5432/db1 -d postgres://user:pass@localhost:5433/db2
```
您可以使用 `psql` 或任何其他 PostgreSQL 客户端连接到 PgDog:
```
psql postgres://pgdog:pgdog@127.0.0.1:6432/pgdog
```
## 🚦 状态 🚦
PgDog 已在生产环境中大规模使用。大多数功能是稳定的,而有些是实验性的。查看[文档](https://docs.pgdog.dev/features/)了解更多详情。新的分片功能几乎每周都会添加。
## 性能
📘 **[架构与基准测试](https://docs.pgdog.dev/architecture/)**
PgDog 针对性能进行了高度优化。我们使用 Rust、[Tokio](https://tokio.rs/)、[bytes crate](https://docs.rs/bytes/latest/bytes/) 来避免不必要的内存分配,并定期分析性能回归。
## 许可证
PgDog 是免费的开源软件,根据 AGPL v3 授权。虽然经常被误解,但此许可证非常宽松,允许以下操作,而不需要您或您的组织承担任何额外要求:
* 内部使用
* 供内部使用的私人修改,无需共享任何源代码
您可以自由使用 PgDog 为您的 PostgreSQL 数据库提供支持,而无需共享任何源代码,包括专有工作成果或您对 PgDog 所做的任何修改。
AGPL 专门为将 PgDog _作为公共服务_提供(例如数据库云提供商)的组织编写,并要求这些组织共享他们对 PgDog 所做的任何修改,包括新功能和错误修复。
## 贡献
请阅读我们的[贡献指南](CONTRIBUTING.md)。标签:AWS, Docker, DPI, PgDog, PostgreSQL, Rust, 中间件, 分布式数据库, 可视化界面, 子域名突变, 安全防御评估, 性能优化, 数据分片, 数据库代理, 数据库扩展, 数据库网关, 数据库运维, 查询路由, 检测绕过, 网络流量审计, 请求拦截, 负载均衡, 连接池, 通知系统, 通知系统