mirror of
https://github.com/Vonng/ddia.git
synced 2024-12-06 15:20:12 +08:00
添加法律说明
This commit is contained in:
parent
2413ea0e66
commit
6a422fa2f0
59
OTHER.md
59
OTHER.md
@ -1,59 +0,0 @@
|
|||||||
# PG
|
|
||||||
|
|
||||||
> 不懂数据库的全栈工程师不是好架构师
|
|
||||||
>
|
|
||||||
> —— Vonng
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [PostgreSQL](pg/)
|
|
||||||
|
|
||||||
PostgreSQL是世界上最先进的开源关系型数据库!
|
|
||||||
|
|
||||||
### SQL
|
|
||||||
|
|
||||||
- [PostgreSQL中的锁](note/pg-sql-lock.md)
|
|
||||||
|
|
||||||
### GIS
|
|
||||||
|
|
||||||
- [PostGIS入门](gis/intro.md) 【EMPTY】
|
|
||||||
|
|
||||||
|
|
||||||
- [PostGIS教程-翻译计划](gis/README.md)
|
|
||||||
|
|
||||||
### Adminstration
|
|
||||||
|
|
||||||
- [Installation](pg/pg-admin-install.md)
|
|
||||||
- [Setup](pg/pg-admin-setup.md)
|
|
||||||
- [Configuration](pg/pg-admin-config.md)
|
|
||||||
- [Authentication](pg/pg-admin-auth.md)
|
|
||||||
- [PostgreSQL 监控](pg/pg-admin-monitor.md)
|
|
||||||
- [PostgreSQL备份与恢复](pg/pg-admin-backup.md)
|
|
||||||
- [PostgreSQL WAL与检查点](pg/pg-admin-wal.md)
|
|
||||||
- [PostgreSQL 高可用](pg/pg-admin-ha.md)
|
|
||||||
|
|
||||||
### Tunning
|
|
||||||
|
|
||||||
- [Memory Tunning](pg/pg-tune-memory.md)
|
|
||||||
- [Checkpoint Tuning Basic](pg/pg-tune-checkpoint)
|
|
||||||
- [Autovacuum Tuning Basics](pg/pg-tune-autovacuum.md)
|
|
||||||
|
|
||||||
### Extension
|
|
||||||
|
|
||||||
- [PostGIS Installation](pg/ext-postgis-install.md)
|
|
||||||
- [FileFDW Usage](pg/ext-file_fdw-intro.md)
|
|
||||||
- [RedisFDW Installation](pg/ext-redis_fdw-install.md)
|
|
||||||
- [MongoFDW Installation](pg/ext-mongo_fdw-install.md)
|
|
||||||
- [PgAdmin Installation](pg/ext-pgadmin-install.md)
|
|
||||||
|
|
||||||
### Reference
|
|
||||||
|
|
||||||
- [PostgreSQL 9.6 中文文档](http://www.postgres.cn/docs/9.6/)
|
|
||||||
- [PostgreSQL 10.1 官方文档](https://www.postgresql.org/docs/10/static/index.html)
|
|
||||||
|
|
||||||
|
|
||||||
- [PostGIS 2.4 官方文档](https://postgis.net/docs/manual-2.4/)
|
|
||||||
|
|
||||||
|
|
||||||
- [Introduction to PostGIS](http://workshops.boundlessgeo.com/postgis-intro/index.html)
|
|
||||||
|
|
54
README.md
54
README.md
@ -87,23 +87,37 @@
|
|||||||
|
|
||||||
精翻可以看,机翻基本没法看,初翻对于业内人士能凑合看。
|
精翻可以看,机翻基本没法看,初翻对于业内人士能凑合看。
|
||||||
|
|
||||||
| 章节 | 计划 | 进度 |
|
| 章节 | 进度 | Credit |
|
||||||
| ---------------------------------- | :--: | ------------ |
|
| :--------------------------------: | :----------: | --------------------------------------------------------- |
|
||||||
| 序言 | | 机翻 |
|
| 序言 | 机翻 | 序言初翻 by [seagullbird](https://github.com/seagullbird) |
|
||||||
| 第一部分:数据系统基础 ——概览 | | 初翻 |
|
| 第一部分:数据系统基础 ——概览 | 初翻 | |
|
||||||
| 第一章:可靠性、可扩展性、可维护性 | | **精翻** |
|
| 第一章:可靠性、可扩展性、可维护性 | **精翻** | |
|
||||||
| 第二章:数据模型与查询语言 | | 初翻 |
|
| 第二章:数据模型与查询语言 | 初翻 | |
|
||||||
| 第三章:存储与检索 | | 初翻 |
|
| 第三章:存储与检索 | 初翻 | |
|
||||||
| 第四章:编码与演化 | | 初翻 |
|
| 第四章:编码与演化 | 初翻 | |
|
||||||
| 第二部分:分布式数据——概览 | | 初翻 |
|
| 第二部分:分布式数据——概览 | 初翻 | |
|
||||||
| 第五章:复制 | | 初翻 |
|
| 第五章:复制 | 初翻 | |
|
||||||
| 第六章:分片 | | 初翻 |
|
| 第六章:分片 | 初翻 | |
|
||||||
| 第七章:事务 | | **精翻 50%** |
|
| 第七章:事务 | **精翻 50%** | |
|
||||||
| 第八章:分布式系统的麻烦 | | 机翻 |
|
| 第八章:分布式系统的麻烦 | 机翻 | |
|
||||||
| 第九章:一致性与共识 | | 机翻 |
|
| 第九章:一致性与共识 | 机翻 | |
|
||||||
| 第三部分:前言 | | 机翻 |
|
| 第三部分:前言 | 机翻 | |
|
||||||
| 第十章:批处理 | | 机翻 |
|
| 第十章:批处理 | 机翻 | |
|
||||||
| 第十一章:流处理 | | 机翻 |
|
| 第十一章:流处理 | 机翻 | |
|
||||||
| 第十二章:数据系统的未来 | | 机翻 |
|
| 第十二章:数据系统的未来 | 机翻 | |
|
||||||
| 术语表 | | - |
|
| 术语表 | - | |
|
||||||
| 后记 | | 机翻 |
|
| 后记 | 机翻 | |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## CONTRIBUTION
|
||||||
|
|
||||||
|
欢迎贡献,精翻后的章节,接受ISSUE指正。
|
||||||
|
|
||||||
|
贡献者需要同意[法律声明](#法律声明)所叙内容,翻译请提前联系以免冲突。
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## LICENSE
|
||||||
|
|
||||||
|
CC-BY 4.0
|
@ -1,4 +1,4 @@
|
|||||||
# 《设计数据密集应用》
|
# 《设计数据密集型应用》中文翻译
|
||||||
|
|
||||||
* 作者: Martin Kleppmann
|
* 作者: Martin Kleppmann
|
||||||
* 原书名称:《Designing Data-Intensive Application》
|
* 原书名称:《Designing Data-Intensive Application》
|
||||||
@ -68,13 +68,13 @@
|
|||||||
|
|
||||||
## 翻译计划
|
## 翻译计划
|
||||||
|
|
||||||
机翻:只在乎结构:梳理文章结构、图片、引用、备注。
|
机翻:关注结构,梳理文章结构、图片、引用、备注。
|
||||||
|
|
||||||
初翻:保证自己经完全理解本章内容,人工修复显著的错误,重新组织语言。
|
初翻:保证自己经完全理解本章内容,人工修复显著的错误,重新组织语言。
|
||||||
|
|
||||||
精翻:确定术语的最终译法,修复格式瑕疵,着力信达雅。
|
精翻:确定术语的最终译法,修复格式瑕疵,着力信达雅。
|
||||||
|
|
||||||
通常机翻一章1个小时左右,初翻一章6小时,精翻一章三到五天。
|
通常机翻一章1个小时左右,初翻一章6小时,精翻一章三五天到一两周不等。
|
||||||
|
|
||||||
精翻可以看,初翻凑合看,机翻没法看。精翻太累了,看心情吧。
|
精翻可以看,初翻凑合看,机翻没法看。精翻太累了,看心情吧。
|
||||||
|
|
||||||
|
@ -1,45 +0,0 @@
|
|||||||
# PostGIS教程
|
|
||||||
|
|
||||||
翻译完DDIA再来看看有没有空搞这个吧。
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Workshop Modules
|
|
||||||
|
|
||||||
- [1. Welcome](http://workshops.boundlessgeo.com/postgis-intro/welcome.html)
|
|
||||||
- [2. Introduction](http://workshops.boundlessgeo.com/postgis-intro/introduction.html)
|
|
||||||
- [3. Installation](http://workshops.boundlessgeo.com/postgis-intro/installation.html)
|
|
||||||
- [4. Creating a Spatial Database](http://workshops.boundlessgeo.com/postgis-intro/creating_db.html)
|
|
||||||
- [5. Loading spatial data](http://workshops.boundlessgeo.com/postgis-intro/loading_data.html)
|
|
||||||
- [6. About our data](http://workshops.boundlessgeo.com/postgis-intro/about_data.html)
|
|
||||||
- [7. Simple SQL](http://workshops.boundlessgeo.com/postgis-intro/simple_sql.html)
|
|
||||||
- [8. Simple SQL Exercises](http://workshops.boundlessgeo.com/postgis-intro/simple_sql_exercises.html)
|
|
||||||
- [9. Geometries](http://workshops.boundlessgeo.com/postgis-intro/geometries.html)
|
|
||||||
- [10. Geometry Exercises](http://workshops.boundlessgeo.com/postgis-intro/geometries_exercises.html)
|
|
||||||
- [11. Spatial Relationships](http://workshops.boundlessgeo.com/postgis-intro/spatial_relationships.html)
|
|
||||||
- [12. Spatial Relationships Exercises](http://workshops.boundlessgeo.com/postgis-intro/spatial_relationships_exercises.html)
|
|
||||||
- [13. Spatial Joins](http://workshops.boundlessgeo.com/postgis-intro/joins.html)
|
|
||||||
- [14. Spatial Joins Exercises](http://workshops.boundlessgeo.com/postgis-intro/joins_exercises.html)
|
|
||||||
- [15. Spatial Indexing](http://workshops.boundlessgeo.com/postgis-intro/indexing.html)
|
|
||||||
- [16. Projecting Data](http://workshops.boundlessgeo.com/postgis-intro/projection.html)
|
|
||||||
- [17. Projection Exercises](http://workshops.boundlessgeo.com/postgis-intro/projection_exercises.html)
|
|
||||||
- [18. Geography](http://workshops.boundlessgeo.com/postgis-intro/geography.html)
|
|
||||||
- [19. Geometry Constructing Functions](http://workshops.boundlessgeo.com/postgis-intro/geometry_returning.html)
|
|
||||||
- [20. More Spatial Joins](http://workshops.boundlessgeo.com/postgis-intro/joins_advanced.html)
|
|
||||||
- [21. Validity](http://workshops.boundlessgeo.com/postgis-intro/validity.html)
|
|
||||||
- [22. Equality](http://workshops.boundlessgeo.com/postgis-intro/equality.html)
|
|
||||||
- [23. Linear Referencing](http://workshops.boundlessgeo.com/postgis-intro/linear_referencing.html)
|
|
||||||
- [24. Dimensionally Extended 9-Intersection Model](http://workshops.boundlessgeo.com/postgis-intro/de9im.html)
|
|
||||||
- [25. Clustering on Indices](http://workshops.boundlessgeo.com/postgis-intro/clusterindex.html)
|
|
||||||
- [26. 3-D](http://workshops.boundlessgeo.com/postgis-intro/3d.html)
|
|
||||||
- [27. Nearest-Neighbour Searching](http://workshops.boundlessgeo.com/postgis-intro/knn.html)
|
|
||||||
- [28. Tracking Edit History using Triggers](http://workshops.boundlessgeo.com/postgis-intro/history_tracking.html)
|
|
||||||
- [29. Advanced Geometry Constructions](http://workshops.boundlessgeo.com/postgis-intro/advanced_geometry_construction.html)
|
|
||||||
- [30. Tuning PostgreSQL for Spatial](http://workshops.boundlessgeo.com/postgis-intro/tuning.html)
|
|
||||||
- [31. PostgreSQL Security](http://workshops.boundlessgeo.com/postgis-intro/security.html)
|
|
||||||
- [32. PostgreSQL Schemas](http://workshops.boundlessgeo.com/postgis-intro/schemas.html)
|
|
||||||
- [33. PostgreSQL Backup and Restore](http://workshops.boundlessgeo.com/postgis-intro/backup.html)
|
|
||||||
- [34. Software Upgrades](http://workshops.boundlessgeo.com/postgis-intro/upgrades.html)
|
|
||||||
- [35. Appendix A: PostGIS Functions](http://workshops.boundlessgeo.com/postgis-intro/postgis-functions.html)
|
|
||||||
- [36. Appendix B: Glossary](http://workshops.boundlessgeo.com/postgis-intro/glossary.html)
|
|
||||||
- [37. Appendix C: License](http://workshops.boundlessgeo.com/postgis-intro/license.html)
|
|
Binary file not shown.
Before Width: | Height: | Size: 2.3 KiB |
52
pg/README.md
52
pg/README.md
@ -1,52 +0,0 @@
|
|||||||
# PostgreSQL Notes
|
|
||||||
|
|
||||||
## SQL Language
|
|
||||||
|
|
||||||
* PostgreSQL字面值
|
|
||||||
|
|
||||||
## Adminstration
|
|
||||||
|
|
||||||
* [安装/install](pg-admin-install.md)
|
|
||||||
* [设置/setup](pg-admin-setup.md)
|
|
||||||
* [配置/config](pg-admin-config.md)
|
|
||||||
* [认证/auth](pg-admin-auth.md)
|
|
||||||
* 角色/role
|
|
||||||
* 数据库管理/database
|
|
||||||
* 本地化
|
|
||||||
* 日常工作
|
|
||||||
* 备份
|
|
||||||
* 高可用、负载均衡、副本
|
|
||||||
* 恢复
|
|
||||||
* [监控](pg-admin-monitor.md)
|
|
||||||
* 磁盘使用
|
|
||||||
* WAL与可靠性
|
|
||||||
* 逻辑复制
|
|
||||||
* 回归测试
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Client Interface
|
|
||||||
|
|
||||||
* Python: psycopg2
|
|
||||||
* Go: lib/pq, go-pg, jackc/pgx
|
|
||||||
|
|
||||||
## Server Programming
|
|
||||||
|
|
||||||
## PostgreSQL Internal
|
|
||||||
|
|
||||||
## Foreign Data Wrapper
|
|
||||||
* [Using FileFDW fetching sysinfo](fdw-file_fdw-intro.md)
|
|
||||||
* [MongoFDW install](fdw-mongo_fdw-install.md)
|
|
||||||
* [RedisFDW install](fdw-redis_fdw-installmd)
|
|
||||||
* [PgAdmin install](gui-pgadmin-install.md)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## PostGIS
|
|
||||||
|
|
||||||
* [PostGIS install](postgis-install.md)
|
|
||||||
|
|
||||||
|
|
||||||
## Tricks
|
|
@ -1,134 +0,0 @@
|
|||||||
# file_fdw妙用无穷——从数据库读取系统信息
|
|
||||||
|
|
||||||
PostgreSQL是最先进的开源数据库,其中一个非常给力的特性就是FDW:外部数据包装器(Foreign Data Wrapper)。通过FDW,用户可以用统一的方式从Pg中访问各类外部数据源。`file_fdw`就是其中随数据库附赠的两个fdw之一。随着pg10的更新,`file_fdw`也添加了一颗赛艇的功能:从程序输出读取。
|
|
||||||
|
|
||||||
小霸王妙用无穷,我们能通过`file_fdw`,轻松查看操作系统信息,拉取网络数据,把各种各样的数据源轻松喂进数据库里统一查看管理。
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 安装与配置
|
|
||||||
|
|
||||||
`file_fdw`是Pg自带的组件,不需要奇怪的配置,在数据库中执行以下命令即可启用`file_fdw`:
|
|
||||||
|
|
||||||
```plsql
|
|
||||||
CREATE EXTENSION file_fdw;
|
|
||||||
```
|
|
||||||
|
|
||||||
启用FDW插件之后,需要创建一个实例,也是一行SQL搞定,创建一个名为`fs`的FDW Server实例。
|
|
||||||
|
|
||||||
```plsql
|
|
||||||
CREATE SERVER fs FOREIGN DATA WRAPPER file_fdw;
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 创建外部表
|
|
||||||
|
|
||||||
举个栗子,如果我想从数据库中读取操作系统中正在运行的进程信息,该怎么做呢?
|
|
||||||
|
|
||||||
最典型,也是最常用的外部数据格式就是CSV啦。不过系统命令输出的结果并不是很规整:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
>>> ps ux
|
|
||||||
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
|
|
||||||
vonng 2658 0.0 0.2 148428 2620 ? S 11:51 0:00 sshd: vonng@pts/0,pts/2
|
|
||||||
vonng 2659 0.0 0.2 115648 2312 pts/0 Ss+ 11:51 0:00 -bash
|
|
||||||
vonng 4854 0.0 0.2 115648 2272 pts/2 Ss 15:46 0:00 -bash
|
|
||||||
vonng 5176 0.0 0.1 150940 1828 pts/2 R+ 16:06 0:00 ps -ux
|
|
||||||
vonng 26460 0.0 1.2 271808 13060 ? S 10月26 0:22 /usr/local/pgsql/bin/postgres
|
|
||||||
vonng 26462 0.0 0.2 271960 2640 ? Ss 10月26 0:00 postgres: checkpointer process
|
|
||||||
vonng 26463 0.0 0.2 271808 2148 ? Ss 10月26 0:25 postgres: writer process
|
|
||||||
vonng 26464 0.0 0.5 271808 5300 ? Ss 10月26 0:27 postgres: wal writer process
|
|
||||||
vonng 26465 0.0 0.2 272216 2096 ? Ss 10月26 0:31 postgres: autovacuum launcher process
|
|
||||||
vonng 26466 0.0 0.1 126896 1104 ? Ss 10月26 0:54 postgres: stats collector process
|
|
||||||
vonng 26467 0.0 0.1 272100 1588 ? Ss 10月26 0:01 postgres: bgworker: logical replication launcher
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
可以通过`awk`,将`ps`的命令输出规整为分隔符为`\x1F`的csv格式。
|
|
||||||
|
|
||||||
```
|
|
||||||
ps aux | awk '{print $1,$2,$3,$4,$5,$6,$7,$8,$9,$10,substr($0,index($0,$11))}' OFS='\037'
|
|
||||||
```
|
|
||||||
|
|
||||||
正戏来啦!通过以下DDL创建一张外表定义
|
|
||||||
|
|
||||||
```plsql
|
|
||||||
CREATE FOREIGN TABLE process_status (
|
|
||||||
username TEXT,
|
|
||||||
pid INTEGER,
|
|
||||||
cpu NUMERIC,
|
|
||||||
mem NUMERIC,
|
|
||||||
vsz BIGINT,
|
|
||||||
rss BIGINT,
|
|
||||||
tty TEXT,
|
|
||||||
stat TEXT,
|
|
||||||
start TEXT,
|
|
||||||
time TEXT,
|
|
||||||
command TEXT
|
|
||||||
) SERVER fs OPTIONS (
|
|
||||||
PROGRAM $$ps aux | awk '{print $1,$2,$3,$4,$5,$6,$7,$8,$9,$10,substr($0,index($0,$11))}' OFS='\037'$$,
|
|
||||||
FORMAT 'csv', DELIMITER E'\037', HEADER 'TRUE');
|
|
||||||
```
|
|
||||||
|
|
||||||
这里,关键是通过`CREATE FOREIGN TABLE OPTIONS (xxxx)`中的`OPTIONS`提供相应的参数,在`PROGRAM`参数中填入上面的命令,pg就会在查询这张表的时候自动执行此命令,并读取其输出。`FORMAT`参数可以指定为`CSV`,`DELIMITER`参数指定为之前使用的`\x1F`,并通过`HEADER 'TRUE'`忽略CSV的第一行
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
那么结果如何呢?
|
|
||||||
|
|
||||||
![](img/file_fdw.png)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 有什么用
|
|
||||||
|
|
||||||
最简单的场景,原本系统指标监控需要编写各种监测脚本,部署在奇奇怪怪的地方。然后定期执行拉取metric,再存进数据库。现在通过file_fdw的方式,可以将感兴趣的指标直接录入数据库表,一步到位,而且维护方便,部署简单,更加可靠。在外表上加上视图,定期拉取聚合,将原本一个监控系统完成的事情,在数据库中一条龙解决了。
|
|
||||||
|
|
||||||
因为可以从程序输出读取结果,因此file_fdw可以与linux生态里各类强大的命令行工具配合使用,发挥出强大的威力。
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 其他栗子
|
|
||||||
|
|
||||||
诸如此类,实际上后来我发现Facebook貌似有一个类似的产品,叫OSQuery,也是干了差不多的事。通过SQL查询操作系统的指标。但明显PostgreSQL这种方法最简单粗暴高效啦,只要定义表结构,和命令数据源就能轻松对接指标数据,用不了一天就能做出一个功能差不多的东西来。
|
|
||||||
|
|
||||||
用于读取系统用户列表的DDL:
|
|
||||||
|
|
||||||
```plsql
|
|
||||||
CREATE FOREIGN TABLE etc_password (
|
|
||||||
username TEXT,
|
|
||||||
password TEXT,
|
|
||||||
user_id INTEGER,
|
|
||||||
group_id INTEGER,
|
|
||||||
user_info TEXT,
|
|
||||||
home_dir TEXT,
|
|
||||||
shell TEXT
|
|
||||||
) SERVER fs OPTIONS (
|
|
||||||
PROGRAM $$awk -F: 'NF && !/^[:space:]*#/ {print $1,$2,$3,$4,$5,$6,$7}' OFS='\037' /etc/passwd$$,
|
|
||||||
FORMAT 'csv', DELIMITER E'\037'
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
用于读取磁盘用量的DDL:
|
|
||||||
|
|
||||||
```plsql
|
|
||||||
CREATE FOREIGN TABLE disk_free (
|
|
||||||
file_system TEXT,
|
|
||||||
blocks_1m BIGINT,
|
|
||||||
used_1m BIGINT,
|
|
||||||
avail_1m BIGINT,
|
|
||||||
capacity TEXT,
|
|
||||||
iused BIGINT,
|
|
||||||
ifree BIGINT,
|
|
||||||
iused_pct TEXT,
|
|
||||||
mounted_on TEXT
|
|
||||||
) SERVER fs OPTIONS (PROGRAM $$df -ml| awk '{print $1,$2,$3,$4,$5,$6,$7,$8,$9}' OFS='\037'$$, FORMAT 'csv', HEADER 'TRUE', DELIMITER E'\037'
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
当然,用file_fdw只是一个很Naive的FDW,譬如这里就只能读,不能改。
|
|
||||||
|
|
||||||
自己编写FDW实现增删改查逻辑也非常简单,例如Multicorn就是使用Python编写FDW的项目。
|
|
||||||
|
|
||||||
SQL over everything,让世界变的更简单~
|
|
@ -1,102 +0,0 @@
|
|||||||
# PostgreSQL MongoDB FDW安装部署
|
|
||||||
|
|
||||||
最近有业务要求通过PostgreSQL FDW去访问MongoDB。开始我觉得这是个很轻松的任务。但接下来的事真是让人恶心的吐了。MongoDB FDW编译起来真是要人命:混乱的依赖,临时下载和Hotpatch,错误的编译参数,以及最过分的是错误的文档。总算,我在生产环境(Linux RHEL7u2)和开发环境(Mac OS X 10.11.5)都编译成功了。赶紧记录下来,省的下次蛋疼。
|
|
||||||
|
|
||||||
## 环境概述
|
|
||||||
理论上编译这套东西,GCC版本至少为4.1。
|
|
||||||
生产环境 (RHEL7.2 + PostgreSQL9.5.3 + GCC 4.8.5)
|
|
||||||
本地环境 (Mac OS X 10.11.5 + PostgreSQL9.5.3 + clang-703.0.31)
|
|
||||||
|
|
||||||
## mongo_fdw的依赖
|
|
||||||
总的来说,能用包管理解决的问题,尽量用包管理解决。
|
|
||||||
[mongo_fdw](https://github.com/EnterpriseDB/mongo_fdw "mongo_fdw")是我们最终要安装的包
|
|
||||||
它的直接依赖有三个
|
|
||||||
* [json-c 0.12](https://github.com/json-c/json-c/tree/json-c-0.12 "json-c 0.12")
|
|
||||||
* [libmongoc-1.3.1](https://github.com/mongodb/mongo-c-driver/tree/r1.3 "libmongoc-1.3.1")
|
|
||||||
* [libbson-1.3.1](https://github.com/mongodb/libbson/tree/r1.3 "libbson-1.3.1")
|
|
||||||
|
|
||||||
总的来说,mongo_fdw是使用mongo提供的C驱动程序完成功能的。所以我们需要安装libbson与libmongoc。其中libmongoc就是MongoDB的C语言驱动库,它依赖于libbson。
|
|
||||||
所以最后的安装顺序是:
|
|
||||||
`libbson` → `libmongoc` → `json-c`→ `mongo_fdw`
|
|
||||||
|
|
||||||
#### 间接依赖
|
|
||||||
默认依赖的GNU Build全家桶,文档是不会告诉你的。
|
|
||||||
下面列出一些比较简单的,可以通过包管理解决的依赖。
|
|
||||||
请一定按照以下顺序安装`GNU Autotools`
|
|
||||||
|
|
||||||
`m4-1.4.17` → `autoconf-2.69` → `automake-1.15` → `libtool-2.4.6` → `pkg-config-0.29.1`。
|
|
||||||
总之,用yum也好,apt也好,homebrew也好,都是一行命令能搞定的事。
|
|
||||||
还有一个依赖是libmongoc的依赖:`openssl-devel`,不要忘记装。
|
|
||||||
|
|
||||||
|
|
||||||
### 安装 `libbson-1.3.1`
|
|
||||||
```bash
|
|
||||||
git clone -b r1.3 https://github.com/mongodb/libbson;
|
|
||||||
cd libbson;
|
|
||||||
git checkout 1.3.1;
|
|
||||||
./autogen.sh;
|
|
||||||
make && sudo make install;
|
|
||||||
make test;
|
|
||||||
```
|
|
||||||
|
|
||||||
### 安装 `libmongoc-1.3.1`
|
|
||||||
```bash
|
|
||||||
git clone -b r1.3 https://github.com/mongodb/mongo-c-driver
|
|
||||||
cd mongo-c-driver;
|
|
||||||
git checkout 1.3.1;
|
|
||||||
./autogen.sh;
|
|
||||||
# 下一步很重要,一定要使用刚才安装好的系统中的libbson。
|
|
||||||
./configure --with-libbson=system;
|
|
||||||
make && sudo make install;
|
|
||||||
```
|
|
||||||
|
|
||||||
这里为什么要使用1.3.1的版本?这也是有讲究的。因为mongo_fdw中默认使用的是1.3.1的mongo-c-driver。但是它在文档里说只要1.0.0+就可以,其实是在放狗屁。mongo-c-driver与libbson版本是一一对应的。1.0.0版本的libbson脑子被驴踢了,使用了超出C99的特性,比如复数类型。要是用了默认版本就傻逼了。
|
|
||||||
|
|
||||||
#### 安装`json-c`
|
|
||||||
首先,我们来解决json-c的问题
|
|
||||||
```
|
|
||||||
git clone https://github.com/json-c/json-c;
|
|
||||||
cd json-c
|
|
||||||
git checkout json-c-0.12
|
|
||||||
```
|
|
||||||
`./configure`完了可不要急着Make,这个版本的json-c编译参数有问题。
|
|
||||||
** 打开Makefile,找到`CFLAGS`,在编译参数后面添加`-fPIC` **
|
|
||||||
这样GCC会生成位置无关代码,不这样做的话mongo_fdw链接会报错。
|
|
||||||
|
|
||||||
|
|
||||||
### 安装 `Mongo FDW`
|
|
||||||
真正恶心的地方来咯。
|
|
||||||
```bash
|
|
||||||
git clone https://github.com/EnterpriseDB/mongo_fdw;
|
|
||||||
```
|
|
||||||
好了,如果这时候想当然的运行`./autogen.sh --with-master`,它就会去重新下一遍上面几个包了……,而且都是从墙外亚马逊的云主机去下。靠谱的方法就是手动一条条的执行autogen里面的命令。
|
|
||||||
|
|
||||||
首先把上面的json-c目录复制到mongo_fdw的根目录内。
|
|
||||||
然后添加libbson和libmongoc的include路径。
|
|
||||||
```
|
|
||||||
export C_INCLUDE_PATH="/usr/local/include/libbson-1.0/:/usr/local/include/libmongoc-1.0:$C_INCLUDE_PATH"
|
|
||||||
```
|
|
||||||
查看`autogen.sh`,发现里面根据`--with-legacy`和`--with-master`的不同选项,会有不同的操作。具体来说,当指定`--with-master`选项时,它会创建一个config.h,里面定义了一个META_DRIVER的宏变量。当有这个宏变量时,mongo_fdw会使用mongoc.h头文件,也就是所谓的“master”,新版的mongo驱动。当没有时,则会使用"mongo.h"头文件,也就是老版的mongo驱动。这里,我们直接`vi config.h`,添加一行
|
|
||||||
```
|
|
||||||
#define META_DRIVER
|
|
||||||
```
|
|
||||||
这时候,基本上才能算万事大吉。
|
|
||||||
在最终build之前,别忘了执行:`ldconfig`
|
|
||||||
```
|
|
||||||
[sudo] ldconfig
|
|
||||||
```
|
|
||||||
回到mongo_fdw根目录`make`,不出意外,这个`mongo_fdw.so`就出来了。
|
|
||||||
|
|
||||||
|
|
||||||
### 试一试吧?
|
|
||||||
```
|
|
||||||
sudo make install;
|
|
||||||
psql
|
|
||||||
admin=# CREATE EXTENSION mongo_fdw;
|
|
||||||
```
|
|
||||||
|
|
||||||
如果提示找不到libmongoc.so和libbson.so,直接把它们丢进pgsql的lib目录即可。
|
|
||||||
```
|
|
||||||
sudo cp /usr/local/lib/libbson* /usr/local/pgsql/lib/
|
|
||||||
sudo cp /usr/local/lib/libmongoc* /usr/local/pgsql/lib/
|
|
||||||
```
|
|
@ -1,68 +0,0 @@
|
|||||||
# PostgreSQL安装手册
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## PgAdmin4的安装与配置
|
|
||||||
|
|
||||||
PgAdmin是一个为PostgreSQL定制设计的GUI。用起来很不错。可以以本地GUI程序或者Web服务的方式运行。因为Retina屏幕下面PgAdmin依赖的GUI组件显示效果有点问题,这里主要介绍如何以Web服务方式(Python Flask)配置运行PgAdmin4。
|
|
||||||
|
|
||||||
### 下载
|
|
||||||
|
|
||||||
PgAdmin可以从官方FTP下载。
|
|
||||||
|
|
||||||
[postgresql网站FTP目录地址](https://ftp.postgresql.org/pub/pgadmin3/pgadmin4)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
wget https://ftp.postgresql.org/pub/pgadmin3/pgadmin4/v1.1/source/pgadmin4-1.1.tar.gz
|
|
||||||
tar -xf pgadmin4-1.1.tar.gz && cd pgadmin4-1.1/
|
|
||||||
```
|
|
||||||
|
|
||||||
也可以从官方[Git Repo](git://git.postgresql.org/git/pgadmin4.git)下载:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git clone git://git.postgresql.org/git/pgadmin4.git
|
|
||||||
cd pgadmin4
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 安装依赖
|
|
||||||
|
|
||||||
首先,需要安装Python,2或者3都可以。这里使用管理员权限安装Anaconda3发行版作为示例。
|
|
||||||
|
|
||||||
首先创建一个虚拟环境,当然直接上物理环境也是可以的……
|
|
||||||
|
|
||||||
```bash
|
|
||||||
conda create -n pgadmin python=3 anaconda
|
|
||||||
```
|
|
||||||
|
|
||||||
根据对应的Python版本,按照对应的依赖文件安装依赖。
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo pip install -r requirements_py3.txt
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 配置选项
|
|
||||||
|
|
||||||
首先执行初始化脚本,创立PgAdmin的管理员用户。
|
|
||||||
```bash
|
|
||||||
python web/setup.py
|
|
||||||
```
|
|
||||||
按照提示输入Email和密码即可。
|
|
||||||
|
|
||||||
|
|
||||||
编辑`web/config.py`,修改默认配置,主要是改监听地址和端口。
|
|
||||||
|
|
||||||
```python
|
|
||||||
DEFAULT_SERVER = 'localhost'
|
|
||||||
DEFAULT_SERVER_PORT = 5050
|
|
||||||
```
|
|
||||||
修改监听地址为`0.0.0.0`以便从任意IP访问。
|
|
||||||
按需修改端口。
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,227 +0,0 @@
|
|||||||
# PostGIS installation with yum
|
|
||||||
|
|
||||||
> 参考<http://www.postgresonline.com/journal/archives/362-An-almost-idiots-guide-to-install-PostgreSQL-9.5,-PostGIS-2.2-and-pgRouting-2.1.0-with-Yum.html>
|
|
||||||
|
|
||||||
### 1. 安装环境
|
|
||||||
|
|
||||||
- CentOS 7
|
|
||||||
- PostgreSQL10
|
|
||||||
- PostGIS2.4
|
|
||||||
- PGROUTING2.5.2
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 2. PostgreSQL10安装
|
|
||||||
|
|
||||||
##### 2.1 确定系统环境
|
|
||||||
|
|
||||||
```
|
|
||||||
uname -a
|
|
||||||
|
|
||||||
Linux localhost.localdomain 3.10.0-693.el7.x86_64 #1 SMP Tue Aug 22 21:09:27 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
|
|
||||||
```
|
|
||||||
|
|
||||||
##### 2.2 安装正确的rpm包
|
|
||||||
|
|
||||||
```
|
|
||||||
rpm -ivh https://download.postgresql.org/pub/repos/yum/10/redhat/rhel-7-x86_64/pgdg-centos10-10-2.noarch.rpm
|
|
||||||
```
|
|
||||||
|
|
||||||
不同的系统使用不同的rpm源,你可以从 <http://yum.postgresql.org/repopackages.php>获取相应的平台链接。
|
|
||||||
|
|
||||||
##### 2.3 查看rpm包是否正确安装
|
|
||||||
|
|
||||||
```
|
|
||||||
yum list | grep pgdg
|
|
||||||
|
|
||||||
pgdg-centos10.noarch 10-2 installed
|
|
||||||
CGAL.x86_64 4.7-1.rhel7 pgdg10
|
|
||||||
CGAL-debuginfo.x86_64 4.7-1.rhel7 pgdg10
|
|
||||||
CGAL-demos-source.x86_64 4.7-1.rhel7 pgdg10
|
|
||||||
CGAL-devel.x86_64 4.7-1.rhel7 pgdg10
|
|
||||||
MigrationWizard.noarch 1.1-3.rhel7 pgdg10
|
|
||||||
...
|
|
||||||
```
|
|
||||||
|
|
||||||
##### 2.4 安装PG
|
|
||||||
|
|
||||||
```
|
|
||||||
yum install -y postgresql10 postgresql10-server postgresql10-libs postgresql10-contrib postgresql10-devel
|
|
||||||
```
|
|
||||||
|
|
||||||
你可以根据需要选择安装相应的rpm包。
|
|
||||||
|
|
||||||
##### 2.5 启动服务
|
|
||||||
|
|
||||||
默认情况下,PG安装目录为`/usr/pgsql-10/`,data目录为`/var/lib/pgsql/`,系统默认创建用户`postgres`
|
|
||||||
|
|
||||||
```
|
|
||||||
passwd postgres # 为系统postgres设置密码
|
|
||||||
su - postgres # 切换到用户postgres
|
|
||||||
/usr/pgsql-10/bin/initdb -D /var/lib/pgsql/10/data/ # 初始化数据库
|
|
||||||
/usr/pgsql-10/bin/pg_ctl -D /var/lib/pgsql/10/data/ -l logfile start # 启动数据库
|
|
||||||
/usr/pgsql-10/bin/psql postgres postgres # 登录
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. PostGIS安装
|
|
||||||
|
|
||||||
```
|
|
||||||
yum install postgis24_10-client postgis24_10
|
|
||||||
```
|
|
||||||
|
|
||||||
> 如果遇到错误如下:
|
|
||||||
>
|
|
||||||
> ```
|
|
||||||
> --> 解决依赖关系完成
|
|
||||||
> 错误:软件包:postgis24_10-client-2.4.2-1.rhel7.x86_64 (pgdg10)
|
|
||||||
> 需要:libproj.so.0()(64bit)
|
|
||||||
> 错误:软件包:postgis24_10-2.4.2-1.rhel7.x86_64 (pgdg10)
|
|
||||||
> 需要:gdal-libs >= 1.9.0
|
|
||||||
> ```
|
|
||||||
> 你可以尝试通过以下命令解决:```yum -y install epel-release```
|
|
||||||
|
|
||||||
### 4. fdw安装
|
|
||||||
|
|
||||||
```
|
|
||||||
yum install ogr_fdw10
|
|
||||||
```
|
|
||||||
|
|
||||||
### 5. pgrouting安装
|
|
||||||
|
|
||||||
```
|
|
||||||
yum install pgrouting_10
|
|
||||||
```
|
|
||||||
|
|
||||||
### 6. 验证测试
|
|
||||||
|
|
||||||
```
|
|
||||||
# 登录pg后执行以下命令,无报错则证明成功
|
|
||||||
CREATE EXTENSION postgis;
|
|
||||||
CREATE EXTENSION postgis_topology;
|
|
||||||
CREATE EXTENSION ogr_fdw;
|
|
||||||
|
|
||||||
SELECT postgis_full_version();
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 一些依赖组件的编译方式
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 编译工具
|
|
||||||
|
|
||||||
此类工具一般系统都自带。
|
|
||||||
|
|
||||||
* GCC与G++,版本至少为`4.x`。
|
|
||||||
* GNU Make,CMake, Autotools
|
|
||||||
* Git
|
|
||||||
|
|
||||||
CentOS下直接通过`sudo yum install gcc gcc-c++ git autoconf automake libtool m4 `安装。
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 必选依赖
|
|
||||||
|
|
||||||
### PostgreSQL
|
|
||||||
|
|
||||||
PostgreSQL是PostGIS的宿主平台。这里以10.1为例。
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### GEOS
|
|
||||||
|
|
||||||
GEOS是Geometry Engine, Open Source的缩写,是一个C++版本的几何库。是PostGIS的核心依赖。
|
|
||||||
|
|
||||||
PostGIS 2.4用到了GEOS 3.7的一些新特性。不过截止到现在,GEOS官方发布的最新版本是3.6.2,3.7版本的GEOS可以通过[Nightly snapshot](http://geos.osgeo.org/snapshots/)获取。所以目前如果希望用到所有新特性,需要从源码编译安装GEOS 3.7。
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 滚动的每日更新,此URL有可能过期,检查这里http://geos.osgeo.org/snapshots/
|
|
||||||
wget -P ./ http://geos.osgeo.org/snapshots/geos-20171211.tar.bz2
|
|
||||||
tar -jxf geos-20171211.tar.bz2
|
|
||||||
cd geos-20171211
|
|
||||||
./configure
|
|
||||||
make
|
|
||||||
sudo make install
|
|
||||||
cd ..
|
|
||||||
```
|
|
||||||
|
|
||||||
### Proj
|
|
||||||
|
|
||||||
为PostGIS提供坐标投影支持,目前最新版本为4.9.3 :[下载](http://proj4.org/download.html)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 此URL有可能过期,检查这里http://proj4.org/download.html
|
|
||||||
wget -P . http://download.osgeo.org/proj/proj-4.9.3.tar.gz
|
|
||||||
tar -zxf proj-4.9.3.tar.gz
|
|
||||||
cd proj-4.9.3
|
|
||||||
make
|
|
||||||
sudo make install
|
|
||||||
```
|
|
||||||
|
|
||||||
### JSON-C
|
|
||||||
|
|
||||||
目前用于导入GeoJSON格式的数据,函数`ST_GeomFromGeoJson`用到了这个库。
|
|
||||||
|
|
||||||
编译`json-c`需要用到`autoconf, automake, libtool`。
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git clone https://github.com/json-c/json-c
|
|
||||||
cd json-c
|
|
||||||
sh autogen.sh
|
|
||||||
|
|
||||||
./configure # --enable-threading
|
|
||||||
make
|
|
||||||
make install
|
|
||||||
```
|
|
||||||
|
|
||||||
### LibXML2
|
|
||||||
|
|
||||||
目前用于导入GML与KML格式的数据,函数`ST_GeomFromGML`和`ST_GeomFromKML`依赖这个库。
|
|
||||||
|
|
||||||
目前可以在这个[FTP](ftp://xmlsoft.org/libxml2/)服务器上搞到,目前使用的版本是`2.9.7`
|
|
||||||
|
|
||||||
```bash
|
|
||||||
tar -zxf libxml2-sources-2.9.7.tar.gz
|
|
||||||
cd libxml2-sources-2.9.7
|
|
||||||
./configure
|
|
||||||
make
|
|
||||||
sudo make install
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### GADL
|
|
||||||
|
|
||||||
```bash
|
|
||||||
wget -P . http://download.osgeo.org/gdal/2.2.3/gdal-2.2.3.tar.gz
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### SFCGAL
|
|
||||||
|
|
||||||
SFCGAL是CGAL的扩展包装,虽说是可选项,但是很多函数都会经常用到,因此这里也需要安装。[下载页面](http://oslandia.github.io/SFCGAL/installation.html)
|
|
||||||
|
|
||||||
SFCGAL依赖的东西比较多。包括`CMake, CGAL, Boost, MPFR, GMP`等,其中,`CGAL`在上面手动安装过了。这里还需要手动安装BOOST
|
|
||||||
|
|
||||||
```bash
|
|
||||||
wget -P . https://github.com/Oslandia/SFCGAL/archive/v1.3.0.tar.gz
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Boost
|
|
||||||
|
|
||||||
Boost是C++的常用库,SFCGAL依赖BOOST,[下载页面](http://www.boost.org)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
wget -P . https://dl.bintray.com/boostorg/release/1.65.1/source/boost_1_65_1.tar.gz
|
|
||||||
tar -zxf boost_1_65_1.tar.gz
|
|
||||||
cd boost_1_65_1
|
|
||||||
./bootstrap.sh
|
|
||||||
./b2
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
|||||||
# Redis FDW 安装
|
|
||||||
|
|
||||||
```
|
|
||||||
# 先安装hiredis
|
|
||||||
git clone https://github.com/redis/hiredis
|
|
||||||
cd hiredis
|
|
||||||
make -j8
|
|
||||||
sudo make install
|
|
||||||
|
|
||||||
# 再安装redis_fdw
|
|
||||||
git clone https://github.com/pg-redis-fdw/redis_fdw
|
|
||||||
cd redis_fdw
|
|
||||||
PGSQL_BIN="/usr/local/pgsql/bin/"
|
|
||||||
git checkout REL9_5_STABLE
|
|
||||||
PATH="$PGSQL_BIN:$PATH" make USE_PGXS=1
|
|
||||||
sudo PATH="$PGSQL_BIN:$PATH" make USE_PGXS=1 install
|
|
||||||
```
|
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 348 KiB |
@ -1,172 +0,0 @@
|
|||||||
# Client Authentication
|
|
||||||
|
|
||||||
连接数据库出现出现奇怪的权限问题时,首先检查`postgresql.conf`
|
|
||||||
|
|
||||||
配置`listen_addresses`为合适的值(例如`*`)以允许外部连接。默认情况下`listen_addresses=localhost`,只允许本地TCP连接。
|
|
||||||
|
|
||||||
相关视图:`pg_hba_file_rules`
|
|
||||||
|
|
||||||
## 认证配置
|
|
||||||
|
|
||||||
* 认证通过配置文件`pg_hba.conf`控制。hba是`host based authentication`的缩写。
|
|
||||||
* `pg_hba.conf`默认在数据目录的顶层,但可以通过`postgresql.conf`中`hba_file`来指明其他位置。
|
|
||||||
* `pg_hba`的配置项为文本文件,每行一条记录
|
|
||||||
* 每条记录包括`<type,database,user,address,method>`五个字段。
|
|
||||||
* 字段间使用空白字符分隔,引号扩起的值内部可以包含空白。
|
|
||||||
* 每条记录的格式为
|
|
||||||
* 什么样的连接方式:`local, host, hostssl, hostnossl`
|
|
||||||
* 什么样的数据库:`all, sameuser, samerole, replication, <dbname>`
|
|
||||||
* 什么样的用户:`all, <username>, +<groupname>`
|
|
||||||
* 什么样的地址:IP地址,CIDR地址,`0.0.0.0/0`表示所有机器。
|
|
||||||
* 什么样的行为:`trust, reject, md5, password, ident, peer...`
|
|
||||||
|
|
||||||
|
|
||||||
下面是这五个字段的可选值
|
|
||||||
|
|
||||||
### 连接方式
|
|
||||||
|
|
||||||
- `local`:使用 Unix 域套接字的连接。如果没有这种类型的记录,就不允许 Unix 域套接字连接。
|
|
||||||
|
|
||||||
- `host`:使用 TCP/IP 建立的连接,可以使用SSL也可以不使用。
|
|
||||||
|
|
||||||
- `hostssl`:使用TCP/IP + SSL连接
|
|
||||||
|
|
||||||
- `hostnossl`:与`hostssl`相反:使用不启用SSL的TCP/IP连接。
|
|
||||||
|
|
||||||
|
|
||||||
### 数据库
|
|
||||||
|
|
||||||
- `<dbname>`:指定记录所匹配的数据库名称。
|
|
||||||
|
|
||||||
- `all`:指定该记录匹配所有数据库。
|
|
||||||
- `sameuser`:指定如果被请求的数据库和请求的用户同名,则匹配。
|
|
||||||
|
|
||||||
### 用户
|
|
||||||
|
|
||||||
指定这条记录匹配哪些数据库用户名。值`all`指定它匹配所有用户。否则,它要么是一个特定数据库用户的名字或者是一个有前导`+`的组名称(回想一下,在PostgreSQL里,用户和组没有真正的区别,`+`实际表示"匹配这个角色的任何直接或间接成员角色",而没有`+`记号的名字只匹配指定的角色)。出于这个目的,如果超级用户显式的是一个角色的成员(直接或间接),那么超级用户将只被认为是该角色的一个成员而不是作为一个超级用户。多个用户名可以通过用逗号分隔的方法提供。一个包含用户名的文件可以通过在文件名前面加上`@`来指定。
|
|
||||||
|
|
||||||
- `*address*`
|
|
||||||
|
|
||||||
指定这个记录匹配的客户端机器地址。这个域可以包含一个主机名、一个 IP 地址范围或下文提到的特殊关键字之一。一个 IP 地址范围以该范围的开始地址的标准数字记号指定,然后是一个斜线(`/`) 和一个CIDR掩码长度。掩码长度表示客户端 IP 地址必须匹配的高序二进制位位数。在给出的 IP 地址中,这个长度的右边的二进制位必须为零。 在 IP 地址、`/`和 CIDR 掩码长度之间不能有空白。这种方法指定一个 IPv4 地址范围的典型例子是: `172.20.143.89/32`用于一个主机, `172.20.143.0/24`用于一个小型网络, `10.6.0.0/16`用于一个大型网络。 一个单主机的 IPv6 地址范围看起来像这样:`::1/128`(IPv6 回环地址), 一个小型网络的 IPv6 地址范围则类似于:`fe80::7a31:c1ff:0000:0000/96`。 `0.0.0.0/0`表示所有 IPv4 地址,并且`::0/0`表示所有 IPv6 地址。要指定一个单一主机,IPv4 用一个长度为 32 的 CIDR 掩码或者 IPv6 用 长度为 128 的 CIDR 掩码。在一个网络地址中,不要省略结尾的零。一个以 IPv4 格式给出的项将只匹配 IPv4 连接并且一个以 IPv6 格式给出的项将只匹配 IPv6 连接,即使对应的地址在 IPv4-in-IPv6 范围内。请注意如果系统的 C 库不支持 IPv6 地址,那么 IPv6 格式中的项将被拒绝。你也可以写`all`来匹配任何 IP 地址、写`samehost`来匹配任何本服务器自身的 IP 地址或者写`samenet`来匹配本服务器直接连接到的任意子网的任意地址。若果指定了一个主机名(任何除 IP 地址单位或特殊关键字之外的都被作为主机名处理), 该名称会与客户端的 IP 地址的反向名字解析(例如使用 DNS 时的反向 DNS 查找)结果进行比较。主机名比较是大小写敏感的。如果匹配上,那么将在主机名上执行一次正向名字解析(例如正向 DNS 查找)来检查它解析到的任何地址是否等于客户端的 IP 地址。如果两个方向都匹配,则该项被认为匹配(`pg_hba.conf`中使用的主机名应该是客户端 IP 地址的地址到名字解析返回的结果,否则该行将不会匹配。某些主机名数据库允许将一个 IP 地址关联多个主机名,但是当被要求解析一个 IP 地址时,操作系统将只返回一个主机名)。一个以点号(`.`)开始的主机名声明匹配实际主机名的后缀。因此`.example.com`将匹配`foo.example.com`(但不匹配`example.com`)。当主机名在`pg_hba.conf`中被指定时,你应该保证名字解析很快。建立一个类似`nscd`的本地名字解析缓存是一种不错的选择。另外,你可能希望启用配置参数`log_hostname`来在日志中查看客户端的主机名而不是 IP 地址。这个域只适用于`host`、 `hostssl`和`hostnossl`记录。用户有时候会疑惑为什么这样处理的主机名看起来很复杂,因为需要两次名字解析(包括一次 客户端 IP 地址的反向查找)。在客户端的反向 DNS 项没有建立或者得到某些意料之外的主机 名的情况下,这种方式会让该特性的使用变得复杂。这样做主要是为了效率:通过这种方式,一次 连接尝试要求最多两次解析器查找,一次逆向以及一次正向。如果有一个解析器对于该地址有问 题,这仅仅是客户端的问题。一种假想的替代实现是只做前向查找,这种方法不得不在每一次连接 尝试期间解析`pg_hba.conf`中提到的每一个主机名。如果列出了很多 名称,这就会很慢。并且如果主机名之一有解析器问题,它会变成所有人的问题。另外,一次反向查找也是实现后缀匹配特性所需的,因为需要知道实际的客户端主机名来与模式进行匹配。注意这种行为与其他流行的基于主机名的访问控制实现相一致,例如 Apache HTTP Server 和 TCP Wrappers。
|
|
||||||
|
|
||||||
- `*IP-address*``*IP-mask*`
|
|
||||||
|
|
||||||
这两个域可以被用作`*IP-address*``/` `*mask-length*`记号法的替代方案。和指定掩码长度不同,实际的掩码被指 定在一个单独的列中。例如,`255.0.0.0`表示 IPv4 CIDR 掩码长度 8,而`255.255.255.255`表示 CIDR 掩码长度 32。这些域只适用于`host`、`hostssl`和`hostnossl`记录。
|
|
||||||
|
|
||||||
`*auth-method*`
|
|
||||||
|
|
||||||
指定当一个连接匹配这个记录时,要使用的认证方法。下面对可能的选择做了概述,详见[第 20.3 节](http://www.postgres.cn/docs/9.6/auth-methods.html)。`trust`无条件地允许连接。这种方法允许任何可以与PostgreSQL数据库服务器连接的用户以他们期望的任意PostgreSQL数据库用户身份登入,而不需要口令或者其他任何认证。详见[第 20.3.1 节](http://www.postgres.cn/docs/9.6/auth-methods.html#AUTH-TRUST)。`reject`无条件地拒绝连接。这有助于从一个组中"过滤出"特定主机,例如一个`reject`行可以阻塞一个特定的主机连接,而后面一行允许一个特定网络中的其余主机进行连接。`md5`要求客户端提供一个双重 MD5 加密的口令进行认证。详见[第 20.3.2 节](http://www.postgres.cn/docs/9.6/auth-methods.html#AUTH-PASSWORD)。`password`要求客户端提供一个未加密的口令进行认证。因为口令是以明文形式在网络上发送的,所以我们不应该在不可信的网络上使用这种方式。详见[第 20.3.2 节](http://www.postgres.cn/docs/9.6/auth-methods.html#AUTH-PASSWORD)。`gss`用 GSSAPI 认证用户。只对 TCP/IP 连接可用。详见[第 20.3.3 节](http://www.postgres.cn/docs/9.6/auth-methods.html#GSSAPI-AUTH)。`sspi`用 SSPI 来认证用户。只在 Windows 上可用。详见[第 20.3.4 节](http://www.postgres.cn/docs/9.6/auth-methods.html#SSPI-AUTH)。`ident`通过联系客户端的 ident 服务器获取客户端的操作系统名,并且检查它是否匹配被请求的数据库用户名。Ident 认证只能在 TCIP/IP 连接上使用。当为本地连接指定这种认证方式时,将用 peer 认证来替代。详见[第 20.3.5 节](http://www.postgres.cn/docs/9.6/auth-methods.html#AUTH-IDENT)。`peer`从操作系统获得客户端的操作系统用户,并且检查它是否匹配被请求的数据库用户名。这只对本地连接可用。详见[第 20.3.6 节](http://www.postgres.cn/docs/9.6/auth-methods.html#AUTH-PEER)。`ldap`使用LDAP服务器认证。详见[第 20.3.7 节](http://www.postgres.cn/docs/9.6/auth-methods.html#AUTH-LDAP)。`radius`用 RADIUS 服务器认证。详见[第 20.3.8 节](http://www.postgres.cn/docs/9.6/auth-methods.html#AUTH-RADIUS)。`cert`使用 SSL 客户端证书认证。详见[第 20.3.9 节](http://www.postgres.cn/docs/9.6/auth-methods.html#AUTH-CERT)。`pam`使用操作系统提供的可插入认证模块服务(PAM)认证。详见[第 20.3.10 节](http://www.postgres.cn/docs/9.6/auth-methods.html#AUTH-PAM)。`bsd`使用由操作系统提供的 BSD 认证服务进行认证。详见[第 20.3.11 节](http://www.postgres.cn/docs/9.6/auth-methods.html#AUTH-BSD)。
|
|
||||||
|
|
||||||
- `*auth-options*`
|
|
||||||
|
|
||||||
在`*auth-method*`域的后面,可以是形如`*name*``=``*value*`的域,它们指定认证方法的选项。关于哪些认证方法可以用哪些选项的细节请见下文。除了下文列出的与方法相关的选项之外,还有一个与方法无关的认证选项`clientcert`,它可以在任何`hostssl`记录中指定。当被设置为`1`时,这个选项要求客户端在认证方法的其他要求之外出示一个有效的(可信的)SSL 证书。
|
|
||||||
|
|
||||||
用`@`结构包括的文件被读作一个名字列表,它们可以用空白或者逗号分隔。注释用`#`引入,就像在`pg_hba.conf`中那样,并且允许嵌套`@`结构。除非跟在`@`后面的文件名是一个绝对路径, 文件名都被认为是相对于包含引用文件的目录。
|
|
||||||
|
|
||||||
因为每一次连接尝试都会顺序地检查`pg_hba.conf`记录,所以这些记录的顺序是非常关键的。通常,靠前的记录有比较严的连接匹配参数和比较弱的认证方法,而靠后的记录有比较松的匹配参数和比较强的认证方法。 例如,我们希望对本地 TCP/IP 连接使用`trust`认证,而对远程 TCP/IP 连接要求口令。在这种情况下为来自于 127.0.0.1 的连接指定`trust`认证的记录将出现在为一个更宽范围的客户端 IP 地址指定口令认证的记录前面。
|
|
||||||
|
|
||||||
在启动以及主服务器进程收到SIGHUP信号时,`pg_hba.conf`文件会被读取。 如果你在活动的系统上编辑了该文件,你将需要通知 postmaster(使用`pg_ctl reload`或`kill -HUP`)重新读取该文件。
|
|
||||||
|
|
||||||
> **提示: **要连接到一个特定数据库,一个用户必须不仅要通过`pg_hba.conf`检查,还必须要有该数据库上的`CONNECT`权限。如果你希望限制哪些用户能够连接到哪些数据库,授予/撤销`CONNECT`权限通常比在`pg_hba.conf`项中设置规则简单。
|
|
||||||
|
|
||||||
[例 20-1](http://www.postgres.cn/docs/9.6/auth-pg-hba-conf.html#EXAMPLE-PG-HBA.CONF)中展示了`pg_hba.conf`项的一些例子。不同认证方法的详情请见下一节。
|
|
||||||
|
|
||||||
**例 20-1. 示例 pg_hba.conf 项**
|
|
||||||
|
|
||||||
```
|
|
||||||
# 允许本地系统上的任何用户
|
|
||||||
# 通过 Unix 域套接字以任意
|
|
||||||
# 数据库用户名连接到任意数据库
|
|
||||||
#(本地连接的默认值)。
|
|
||||||
#
|
|
||||||
# TYPE DATABASE USER ADDRESS METHOD
|
|
||||||
local all all trust
|
|
||||||
|
|
||||||
# 相同的规则,但是使用本地环回 TCP/IP 连接。
|
|
||||||
#
|
|
||||||
# TYPE DATABASE USER ADDRESS METHOD
|
|
||||||
host all all 127.0.0.1/32 trust
|
|
||||||
|
|
||||||
# 和前一行相同,但是使用了一个独立的掩码列
|
|
||||||
#
|
|
||||||
# TYPE DATABASE USER IP-ADDRESS IP-MASK METHOD
|
|
||||||
host all all 127.0.0.1 255.255.255.255 trust
|
|
||||||
|
|
||||||
# IPv6 上相同的规则
|
|
||||||
#
|
|
||||||
# TYPE DATABASE USER ADDRESS METHOD
|
|
||||||
host all all ::1/128 trust
|
|
||||||
|
|
||||||
# 使用主机名的相同规则(通常同时覆盖 IPv4 和 IPv6)。
|
|
||||||
#
|
|
||||||
# TYPE DATABASE USER ADDRESS METHOD
|
|
||||||
host all all localhost trust
|
|
||||||
|
|
||||||
# 允许来自任意具有 IP 地址
|
|
||||||
# 192.168.93.x 的主机上任意
|
|
||||||
# 用户以 ident 为该连接所
|
|
||||||
# 报告的相同用户名连接到
|
|
||||||
# 数据库 "postgres"
|
|
||||||
#(通常是操作系统用户名)。
|
|
||||||
#
|
|
||||||
# TYPE DATABASE USER ADDRESS METHOD
|
|
||||||
host postgres all 192.168.93.0/24 ident
|
|
||||||
|
|
||||||
# 如果用户的口令被正确提供,
|
|
||||||
# 允许来自主机 192.168.12.10
|
|
||||||
# 的任意用户连接到数据库 "postgres"。
|
|
||||||
#
|
|
||||||
# TYPE DATABASE USER ADDRESS METHOD
|
|
||||||
host postgres all 192.168.12.10/32 md5
|
|
||||||
|
|
||||||
# 如果用户的口令被正确提供,
|
|
||||||
# 允许 example.com 中主机上
|
|
||||||
# 的任意用户连接到任意数据库。
|
|
||||||
#
|
|
||||||
# TYPE DATABASE USER ADDRESS METHOD
|
|
||||||
host all all .example.com md5
|
|
||||||
|
|
||||||
# 如果没有前面的 "host" 行,这两
|
|
||||||
# 行将拒绝所有来自 192.168.54.1
|
|
||||||
# 的连接(因为那些项将首先被匹配),
|
|
||||||
# 但是允许来自互联网其他任何地方的
|
|
||||||
# GSSAPI 连接。零掩码导致主机
|
|
||||||
# IP 地址中的所有位都不会被考虑,
|
|
||||||
# 因此它匹配任意主机。
|
|
||||||
#
|
|
||||||
# TYPE DATABASE USER ADDRESS METHOD
|
|
||||||
host all all 192.168.54.1/32 reject
|
|
||||||
host all all 0.0.0.0/0 gss
|
|
||||||
|
|
||||||
# 允许来自 192.168.x.x 主机的用户
|
|
||||||
# 连接到任意数据库,如果它们能够
|
|
||||||
# 通过 ident 检查。例如,假设 ident
|
|
||||||
# 说用户是 "bryanh" 并且他要求以
|
|
||||||
# PostgreSQL 用户 "guest1" 连接,
|
|
||||||
# 如果在 pg_ident.conf 有一个映射
|
|
||||||
# "omicron" 的选项说 "bryanh" 被
|
|
||||||
# 允许以 "guest1" 连接,则该连接将被允许。
|
|
||||||
#
|
|
||||||
# TYPE DATABASE USER ADDRESS METHOD
|
|
||||||
host all all 192.168.0.0/16 ident map=omicron
|
|
||||||
|
|
||||||
# 如果这些是本地连接的唯一三行,
|
|
||||||
# 它们将允许本地用户只连接到它们
|
|
||||||
# 自己的数据库(与其数据库用户名
|
|
||||||
# 同名的数据库),不过管理员和角
|
|
||||||
# 色 "support" 的成员除外(它们可
|
|
||||||
# 以连接到所有数据库)。文件
|
|
||||||
# $PGDATA/admins 包含一个管理员
|
|
||||||
# 名字的列表。在所有情况下都要求口令。
|
|
||||||
#
|
|
||||||
# TYPE DATABASE USER ADDRESS METHOD
|
|
||||||
local sameuser all md5
|
|
||||||
local all @admins md5
|
|
||||||
local all +support md5
|
|
||||||
|
|
||||||
# 上面的最后两行可以被整合为一行:
|
|
||||||
local all @admins,+support md5
|
|
||||||
|
|
||||||
# 数据库列也可以用列表和文件名:
|
|
||||||
local db1,db2,@demodbs all md5
|
|
||||||
```
|
|
@ -1,701 +0,0 @@
|
|||||||
---
|
|
||||||
author: "Vonng"
|
|
||||||
description: "PostgreSQL备份与恢复"
|
|
||||||
categories: ["DBA"]
|
|
||||||
tags: ["PostgreSQL","Admin"]
|
|
||||||
type: "post"
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# PostgreSQL备份与恢复
|
|
||||||
|
|
||||||
备份是DBA的安身立命之本,有备份,就不用慌。
|
|
||||||
|
|
||||||
备份有三种形式:SQL转储,文件系统备份,连续归档
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 1. SQL转储
|
|
||||||
|
|
||||||
SQL 转储方法的思想是:
|
|
||||||
|
|
||||||
创建一个由SQL命令组成的文件,服务器能利用其中的SQL命令重建与转储时状态一样的数据库。
|
|
||||||
|
|
||||||
### 1.1 转储
|
|
||||||
|
|
||||||
工具`pg_dump`、`pg_dumpall`用于进行SQL转储。结果输出到stdout。
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pg_dump dbname > filename
|
|
||||||
pg_dump dbname -f filename
|
|
||||||
```
|
|
||||||
|
|
||||||
* `pg_dump`是一个普通的PostgreSQL客户端应用。可以在任何可以访问该数据库的远端主机上进行备份工作。
|
|
||||||
* `pg_dump`不会以任何特殊权限运行,必须要有你想备份的表的读权限,同时它也遵循同样的HBA机制。
|
|
||||||
* 要备份整个数据库,几乎总是需要一个数据库超级用户。
|
|
||||||
* 该备份方式的重要优势是,它是跨版本、跨机器架构的备份方式。(最远回溯至7.0)
|
|
||||||
* `pg_dump`的备份是内部一致的,是转储开始时刻的数据库快照,转储期间的更新不被包括在内。
|
|
||||||
* `pg_dump`不会阻塞其他数据库操作,但需要排它锁的命令除外(例如大多数 ALTER TABLE)
|
|
||||||
|
|
||||||
### 1.2 恢复
|
|
||||||
|
|
||||||
文本转储文件可由psql读取,从转储中恢复的常用命令是:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
psql dbname < infile
|
|
||||||
```
|
|
||||||
|
|
||||||
* 这条命令不会创建数据库`dbname`,必须在执行psql前自己从`template0`创建。例如,用命令`createdb -T template0 dbname`。默认`template1`和`template0`是一样的,新创建的数据库默认以`template1`为模板。
|
|
||||||
|
|
||||||
`CREATE DATABASE dbname TEMPLATE template0;`
|
|
||||||
|
|
||||||
* 非文本文件转储可以使用[pg_restore](http://www.postgres.cn/docs/9.6/app-pgrestore.html)工具来恢复。
|
|
||||||
|
|
||||||
* 在开始恢复之前,转储库中对象的拥有者以及在其上被授予了权限的用户必须已经存在。如果它们不存在,那么恢复过程将无法将对象创建成具有原来的所属关系以及权限(有时候这就是你所需要的,但通常不是)。
|
|
||||||
|
|
||||||
* 恢复时遇到错误自动终止,则可以设置`ON_ERROR_STOP`变量来运行psql,遇到SQL错误后退出并返回状态3:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
psql --set ON_ERROR_STOP=on dbname < infile
|
|
||||||
```
|
|
||||||
|
|
||||||
* 恢复时可以使用单个事务来保证要么完全正确恢复,要么完全回滚。使用`-1`或`--single-transaction`
|
|
||||||
* pg_dump和psql可以通过管道on-the-fly做转储与恢复
|
|
||||||
|
|
||||||
```
|
|
||||||
pg_dump -h host1 dbname | psql -h host2 dbname
|
|
||||||
```
|
|
||||||
|
|
||||||
### 1.3 全局转储
|
|
||||||
|
|
||||||
一些信息属于数据库集簇,而不是单个数据库的,例如角色、表空间。如果希望转储这些,可使用`pg_dumpall`
|
|
||||||
|
|
||||||
```
|
|
||||||
pg_dumpall > outfile
|
|
||||||
```
|
|
||||||
|
|
||||||
如果只想要全局的数据(角色与表空间),则可以使用`-g, --globals-only`参数。
|
|
||||||
|
|
||||||
转储的结果可以使用psql恢复,通常将转储载入到一个空集簇中可以用`postgres`作为数据库名
|
|
||||||
|
|
||||||
```
|
|
||||||
psql -f infile postgres
|
|
||||||
```
|
|
||||||
|
|
||||||
* 在恢复一个pg_dumpall转储时常常需要具有数据库超级用户访问权限,因为它需要恢复角色和表空间信息。
|
|
||||||
* 如果使用了表空间,请确保转储中的表空间路径适合于新的安装。
|
|
||||||
* pg_dumpall工作步骤是,先创建角色、表空间转储,再为每一个数据库做pg_dump。这意味着每个数据库自身是一致的,但是不同数据库的快照并不同步。
|
|
||||||
|
|
||||||
### 1.4 命令实践
|
|
||||||
|
|
||||||
准备环境,创建测试数据库
|
|
||||||
|
|
||||||
```bash
|
|
||||||
psql postgres -c "CREATE DATABASE testdb;"
|
|
||||||
psql postgres -c "CREATE ROLE test_user LOGIN;"
|
|
||||||
psql testdb -c "CREATE TABLE test_table(i INTEGER);"
|
|
||||||
psql testdb -c "INSERT INTO test_table SELECT generate_series(1,16);"
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# dump到本地文件
|
|
||||||
pg_dump testdb -f testdb.sql
|
|
||||||
|
|
||||||
# dump并用xz压缩,-c指定从stdio接受,-d指定解压模式
|
|
||||||
pg_dump testdb | xz -cd > testdb.sql.xz
|
|
||||||
|
|
||||||
# dump,压缩,分割为1m的小块
|
|
||||||
pg_dump testdb | xz | split -b 1m - testdb.sql.xz
|
|
||||||
cat testdb.sql.xz* | xz -cd | psql # 恢复
|
|
||||||
|
|
||||||
# pg_dump 常用参数参考
|
|
||||||
-s --schema-only
|
|
||||||
-a --data-only
|
|
||||||
-t --table
|
|
||||||
-n --schema
|
|
||||||
-c --clean
|
|
||||||
-f --file
|
|
||||||
|
|
||||||
--inserts
|
|
||||||
--if-exists
|
|
||||||
-N --exclude-schema
|
|
||||||
-T --exclude-table
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 2. 文件系统转储
|
|
||||||
|
|
||||||
SQL 转储方法的思想是:拷贝数据目录的所有文件。为了得到一个可用的备份,所有备份文件都应当保持一致。
|
|
||||||
|
|
||||||
所以通常比而且为了得到一个可用的备份,所有备份文件都应当保持一致。
|
|
||||||
|
|
||||||
* 文件系统拷贝不做逻辑解析,只是简单拷贝文件。好处是执行快,省掉了逻辑解析和重建索引的时间,坏处是占用空间更大,而且只能用于整个数据库集簇的备份
|
|
||||||
|
|
||||||
- 最简单的方式:停机,直接拷贝数据目录的所有文件。
|
|
||||||
|
|
||||||
|
|
||||||
- 有办法通过文件系统(例如xfs)获得一致的冻结快照也可以不停机,但wal和数据目录必须是一致的。
|
|
||||||
- 可以通过制作pg_basebackup进行远程归档备份,可以不停机。
|
|
||||||
|
|
||||||
|
|
||||||
- 可以通过停机执行rsync的方式向远端增量同步数据变更。
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 3. PITR 连续归档与时间点恢复
|
|
||||||
|
|
||||||
Pg在运行中会不断产生WAL,WAL记录了操作日志,从某一个基础的全量备份开始回放后续的WAL,就可以恢复数据库到任意的时刻的状态。为了实现这样的功能,就需要配置WAL归档,将数据库生成的WAL不断保存起来。
|
|
||||||
|
|
||||||
WAL在逻辑上是一段无限的字节流。`pg_lsn`类型(bigint)可以标记WAL中的位置,`pg_lsn`代表一个WAL中的字节位置偏移量。但实践中WAL不是连续的一个文件,而被分割为每16MB一段。
|
|
||||||
|
|
||||||
WAL文件名是有规律的,而且归档时不允许更改。通常为24位十六进制数字,`000000010000000000000003`,其中前面8位十六进制数字表示时间线,后面的16位表示16MB块的序号。即`lsn >> 24`的值。
|
|
||||||
|
|
||||||
查看`pg_lsn`时,例如`0/84A8300`,只要去掉最后六位hex,就可以得到WAL文件序号的后面部分,这里,也就是`8`,如果使用的是默认时间线1,那么对应的WAL文件就是`000000010000000000000008`。
|
|
||||||
|
|
||||||
### 3.1 准备环境
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 目录:
|
|
||||||
# 使用/var/lib/pgsql/data 作为主库目录,使用/var/lib/pgsql/wal作为日志归档目录
|
|
||||||
# sudo mkdir /var/lib/pgsql && sudo chown postgres:postgres /var/lib/pgsql/
|
|
||||||
pg_ctl stop -D /var/lib/pgsql/data
|
|
||||||
rm -rf /var/lib/pgsql/{data,wal} && mkdir -p /var/lib/pgsql/{data,wal}
|
|
||||||
|
|
||||||
# 初始化:
|
|
||||||
# 初始化主库并修改配置文件
|
|
||||||
pg_ctl -D /var/lib/pgsql/data init
|
|
||||||
|
|
||||||
# 配置文件
|
|
||||||
# 创建默认额外配置文件夹,并在postgresql.conf中配置include_dir
|
|
||||||
mkdir -p /var/lib/pgsql/data/conf.d
|
|
||||||
cat >> /var/lib/pgsql/data/postgresql.conf <<- 'EOF'
|
|
||||||
include_dir = 'conf.d'
|
|
||||||
EOF
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3.2 配置自动归档命令
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 归档配置
|
|
||||||
# %p 代表 src wal path, %f 代表 filename
|
|
||||||
cat > /var/lib/pgsql/data/conf.d/archive.conf <<- 'EOF'
|
|
||||||
archive_mode = on
|
|
||||||
archive_command = 'conf.d/archive.sh %p %f'
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# 归档脚本
|
|
||||||
cat > /var/lib/pgsql/data/conf.d/archive.sh <<- 'EOF'
|
|
||||||
test ! -f /var/lib/pgsql/wal/${2} && cp ${1} /var/lib/pgsql/wal/${2}
|
|
||||||
EOF
|
|
||||||
chmod a+x /var/lib/pgsql/data/conf.d/archive.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
归档脚本可以简单到只是一个`cp`,也可以非常复杂。但需要注意以下事项:
|
|
||||||
|
|
||||||
- 归档命令使用数据库用户`postgres`执行,最好放在0700的目录下面。
|
|
||||||
- 归档命令应当拒绝覆盖现有文件,出现覆盖时,返回一个错误代码。
|
|
||||||
- 归档命令可以通过reload配置更新。
|
|
||||||
|
|
||||||
|
|
||||||
- 处理归档失败时的情形
|
|
||||||
|
|
||||||
- 归档文件应当保留原有文件名。
|
|
||||||
|
|
||||||
- WAL不会记录对配置文件的变更。
|
|
||||||
|
|
||||||
- 归档命令中:`%p` 会替换为生成待归档WAL的路径,而`%f`会替换为待归档WAL的文件名
|
|
||||||
|
|
||||||
- 归档脚本可以使用更复杂的逻辑,例如下面的归档命令,在归档目录中每天创建一个以日期YYYYMMDD命名的文件夹,在每天12点移除前一天的归档日志。每天的归档日志使用xz压缩存储。
|
|
||||||
|
|
||||||
```bash
|
|
||||||
wal_dir=/var/lib/pgsql/wal;
|
|
||||||
[[ $(date +%H%M) == 1200 ]] && rm -rf ${wal_dir}/$(date -d"yesterday" +%Y%m%d); /bin/mkdir -p ${wal_dir}/$(date +%Y%m%d) && \
|
|
||||||
test ! -f ${wal_dir}/ && \
|
|
||||||
xz -c %p > ${wal_dir}/$(date +%Y%m%d)/%f.xz
|
|
||||||
```
|
|
||||||
|
|
||||||
- 归档也可以使用外部专用备份工具进行。例如`pgbackrest`与`barman`等。
|
|
||||||
|
|
||||||
|
|
||||||
### 3.3 测试归档
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 启动数据库
|
|
||||||
pg_ctl -D /var/lib/pgsql/data start
|
|
||||||
|
|
||||||
# 确认配置
|
|
||||||
psql postgres -c "SELECT name,setting FROM pg_settings where name like '%archive%';"
|
|
||||||
```
|
|
||||||
|
|
||||||
在当前shell开启监视循环,不断查询WAL的位置,以及归档目录和`pg_wal`中的文件变化
|
|
||||||
|
|
||||||
```bash
|
|
||||||
for((i=0;i<100;i++)) do
|
|
||||||
sleep 1 && \
|
|
||||||
ls /var/lib/pgsql/data/pg_wal && ls /var/lib/pgsql/data/pg_wal/archive_status/
|
|
||||||
psql postgres -c 'SELECT pg_current_wal_lsn() as current, pg_current_wal_insert_lsn() as insert, pg_current_wal_flush_lsn() as flush;'
|
|
||||||
done
|
|
||||||
```
|
|
||||||
|
|
||||||
在另一个Shell中创建一张测试表`foobar`,包含单一的时间戳列,并引入负载,每秒写入一万条记录:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
psql postgres -c 'CREATE TABLE foobar(ts TIMESTAMP);'
|
|
||||||
for((i=0;i<1000;i++)) do
|
|
||||||
sleep 1 && \
|
|
||||||
psql postgres -c 'INSERT INTO foobar SELECT now() FROM generate_series(1,10000)' && \
|
|
||||||
psql postgres -c 'SELECT pg_current_wal_lsn() as current, pg_current_wal_insert_lsn() as insert, pg_current_wal_flush_lsn() as flush;'
|
|
||||||
done
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 自然切换WAL
|
|
||||||
|
|
||||||
可以看到,当WAL LSN的位置超过16M(可以由后6个hex表示)之后,就会rotate到一个新的WAL文件,归档命令会将写完的WAL归档。
|
|
||||||
|
|
||||||
```bash
|
|
||||||
000000010000000000000001 archive_status
|
|
||||||
current | insert | flush
|
|
||||||
-----------+-----------+-----------
|
|
||||||
0/1FC2630 | 0/1FC2630 | 0/1FC2630
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
# rotate here
|
|
||||||
|
|
||||||
000000010000000000000001 000000010000000000000002 archive_status
|
|
||||||
000000010000000000000001.done
|
|
||||||
current | insert | flush
|
|
||||||
-----------+-----------+-----------
|
|
||||||
0/205F1B8 | 0/205F1B8 | 0/205F1B8
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 手工切换WAL
|
|
||||||
|
|
||||||
再开启一个Shell,执行`pg_switch_wal`,强制写入一个新的WAL文件
|
|
||||||
|
|
||||||
```bash
|
|
||||||
psql postgres -c 'SELECT pg_switch_wal();'
|
|
||||||
```
|
|
||||||
|
|
||||||
可以看到,虽然位置才到`32C1D68`,但立即就跳到了下一个16MB的边界点。
|
|
||||||
|
|
||||||
```bash
|
|
||||||
000000010000000000000001 000000010000000000000002 000000010000000000000003 archive_status
|
|
||||||
000000010000000000000001.done 000000010000000000000002.done
|
|
||||||
current | insert | flush
|
|
||||||
-----------+-----------+-----------
|
|
||||||
0/32C1D68 | 0/32C1D68 | 0/32C1D68
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
# switch here
|
|
||||||
|
|
||||||
000000010000000000000001 000000010000000000000002 000000010000000000000003 archive_status
|
|
||||||
000000010000000000000001.done 000000010000000000000002.done 000000010000000000000003.done
|
|
||||||
current | insert | flush
|
|
||||||
-----------+-----------+-----------
|
|
||||||
0/4000000 | 0/4000028 | 0/4000000
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
000000010000000000000001 000000010000000000000002 000000010000000000000003 000000010000000000000004 archive_status
|
|
||||||
000000010000000000000001.done 000000010000000000000002.done 000000010000000000000003.done
|
|
||||||
current | insert | flush
|
|
||||||
-----------+-----------+-----------
|
|
||||||
0/409CBA0 | 0/409CBA0 | 0/409CBA0
|
|
||||||
(1 row)
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 强制kill数据库
|
|
||||||
|
|
||||||
数据库因为故障异常关闭,重启之后,会从最近的检查点,也就是`0/2FB0160`开始重放WAL。
|
|
||||||
|
|
||||||
```bash
|
|
||||||
[17:03:37] vonng@vonng-mac /var/lib/pgsql
|
|
||||||
$ ps axu | grep postgres | grep data | awk '{print $2}' | xargs kill -9
|
|
||||||
|
|
||||||
[17:06:31] vonng@vonng-mac /var/lib/pgsql
|
|
||||||
$ pg_ctl -D /var/lib/pgsql/data start
|
|
||||||
pg_ctl: another server might be running; trying to start server anyway
|
|
||||||
waiting for server to start....2018-01-25 17:07:27.063 CST [9762] LOG: listening on IPv6 address "::1", port 5432
|
|
||||||
2018-01-25 17:07:27.063 CST [9762] LOG: listening on IPv4 address "127.0.0.1", port 5432
|
|
||||||
2018-01-25 17:07:27.064 CST [9762] LOG: listening on Unix socket "/tmp/.s.PGSQL.5432"
|
|
||||||
2018-01-25 17:07:27.078 CST [9763] LOG: database system was interrupted; last known up at 2018-01-25 17:06:01 CST
|
|
||||||
2018-01-25 17:07:27.117 CST [9763] LOG: database system was not properly shut down; automatic recovery in progress
|
|
||||||
2018-01-25 17:07:27.120 CST [9763] LOG: redo starts at 0/2FB0160
|
|
||||||
2018-01-25 17:07:27.722 CST [9763] LOG: invalid record length at 0/49CBE78: wanted 24, got 0
|
|
||||||
2018-01-25 17:07:27.722 CST [9763] LOG: redo done at 0/49CBE50
|
|
||||||
2018-01-25 17:07:27.722 CST [9763] LOG: last completed transaction was at log time 2018-01-25 17:06:30.158602+08
|
|
||||||
2018-01-25 17:07:27.741 CST [9762] LOG: database system is ready to accept connections
|
|
||||||
done
|
|
||||||
server started
|
|
||||||
```
|
|
||||||
|
|
||||||
至此,WAL归档已经确认可以正常工作了。
|
|
||||||
|
|
||||||
### 3.4 制作基础备份
|
|
||||||
|
|
||||||
首先,查看当前WAL的位置:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ psql postgres -c 'SELECT pg_current_wal_lsn() as current, pg_current_wal_insert_lsn() as insert, pg_current_wal_flush_lsn() as flush;'
|
|
||||||
|
|
||||||
current | insert | flush
|
|
||||||
-----------+-----------+-----------
|
|
||||||
0/49CBF20 | 0/49CBF20 | 0/49CBF20
|
|
||||||
```
|
|
||||||
|
|
||||||
使用`pg_basebackup`制作基础备份
|
|
||||||
|
|
||||||
```bash
|
|
||||||
psql postgres -c 'SELECT now();'
|
|
||||||
pg_basebackup -Fp -Pv -Xs -c fast -D /var/lib/pgsql/bkup
|
|
||||||
|
|
||||||
# 常用选项
|
|
||||||
-D : 必选项,基础备份的位置。
|
|
||||||
-Fp : 备份格式: plain 普通文件 tar 归档文件
|
|
||||||
-Pv : -P 启用进度报告 -v 启用详细输出
|
|
||||||
-Xs : 在备份中包括备份期间产生的WAL日志 f:备份完后拉取 s:备份时流式传输
|
|
||||||
-c : fast 立即执行Checkpoint而不是均摊IO spread:均摊IO
|
|
||||||
-R : 设置recovery.conf
|
|
||||||
```
|
|
||||||
|
|
||||||
制作基础备份时,会立即创建一个检查点使得所有脏数据页落盘。
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ pg_basebackup -Fp -Pv -Xs -c fast -D /var/lib/pgsql/bkup
|
|
||||||
pg_basebackup: initiating base backup, waiting for checkpoint to complete
|
|
||||||
pg_basebackup: checkpoint completed
|
|
||||||
pg_basebackup: write-ahead log start point: 0/5000028 on timeline 1
|
|
||||||
pg_basebackup: starting background WAL receiver
|
|
||||||
45751/45751 kB (100%), 1/1 tablespace
|
|
||||||
pg_basebackup: write-ahead log end point: 0/50000F8
|
|
||||||
pg_basebackup: waiting for background process to finish streaming ...
|
|
||||||
pg_basebackup: base backup completed
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 3.5 使用备份
|
|
||||||
|
|
||||||
#### 直接使用
|
|
||||||
|
|
||||||
最简单的使用方式,就是直接用`pg_ctl`启动它。
|
|
||||||
|
|
||||||
当`recovery.conf`不存在时,这样做会启动一个新的完整数据库实例,原原本本地保留了备份完成时的状态。数据库会并不会意识到自己是一个备份。而是以为自己上次没有正常关闭,应用`pg_wal`目录中自带的WAL进行修复,正常重启。
|
|
||||||
|
|
||||||
基础的全量备份可能每天或每周备份一次,要想恢复到最新的时刻,需要和WAL归档配合使用。
|
|
||||||
|
|
||||||
#### 使用WAL归档追赶进度
|
|
||||||
|
|
||||||
可以在备份中数据库下创建一个`recovery.conf`文件,并指定`restore_command`选项。这样的话,当使用`pg_ctl`启动这个数据目录时,postgres会依次拉取所需的WAL,直到没有了为止。
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cat >> /var/lib/pgsql/bkup/recovery.conf <<- 'EOF'
|
|
||||||
restore_command = 'cp /var/lib/pgsql/wal/%f %p'
|
|
||||||
EOF
|
|
||||||
```
|
|
||||||
|
|
||||||
继续在原始主库中执行负载,这时候WAL的进度已经到了`0/9060CE0`,而制作备份的时候位置还在`0/5000028`。
|
|
||||||
|
|
||||||
启动备份之后,可以发现,备份数据库自动从归档文件夹拉取了5~8号WAL并应用。
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ pg_ctl start -D /var/lib/pgsql/bkup -o '-p 5433'
|
|
||||||
waiting for server to start....2018-01-25 17:35:35.001 CST [10862] LOG: listening on IPv6 address "::1", port 5433
|
|
||||||
2018-01-25 17:35:35.001 CST [10862] LOG: listening on IPv4 address "127.0.0.1", port 5433
|
|
||||||
2018-01-25 17:35:35.002 CST [10862] LOG: listening on Unix socket "/tmp/.s.PGSQL.5433"
|
|
||||||
2018-01-25 17:35:35.016 CST [10863] LOG: database system was interrupted; last known up at 2018-01-25 17:21:15 CST
|
|
||||||
2018-01-25 17:35:35.051 CST [10863] LOG: starting archive recovery
|
|
||||||
2018-01-25 17:35:35.063 CST [10863] LOG: restored log file "000000010000000000000005" from archive
|
|
||||||
2018-01-25 17:35:35.069 CST [10863] LOG: redo starts at 0/5000028
|
|
||||||
2018-01-25 17:35:35.069 CST [10863] LOG: consistent recovery state reached at 0/50000F8
|
|
||||||
2018-01-25 17:35:35.070 CST [10862] LOG: database system is ready to accept read only connections
|
|
||||||
done
|
|
||||||
server started
|
|
||||||
2018-01-25 17:35:35.081 CST [10863] LOG: restored log file "000000010000000000000006" from archive
|
|
||||||
$ 2018-01-25 17:35:35.924 CST [10863] LOG: restored log file "000000010000000000000007" from archive
|
|
||||||
2018-01-25 17:35:36.783 CST [10863] LOG: restored log file "000000010000000000000008" from archive
|
|
||||||
cp: /var/lib/pgsql/wal/000000010000000000000009: No such file or directory
|
|
||||||
2018-01-25 17:35:37.604 CST [10863] LOG: redo done at 0/8FFFF90
|
|
||||||
2018-01-25 17:35:37.604 CST [10863] LOG: last completed transaction was at log time 2018-01-25 17:30:39.107943+08
|
|
||||||
2018-01-25 17:35:37.614 CST [10863] LOG: restored log file "000000010000000000000008" from archive
|
|
||||||
cp: /var/lib/pgsql/wal/00000002.history: No such file or directory
|
|
||||||
2018-01-25 17:35:37.629 CST [10863] LOG: selected new timeline ID: 2
|
|
||||||
cp: /var/lib/pgsql/wal/00000001.history: No such file or directory
|
|
||||||
2018-01-25 17:35:37.678 CST [10863] LOG: archive recovery complete
|
|
||||||
2018-01-25 17:35:37.783 CST [10862] LOG: database system is ready to accept connections
|
|
||||||
```
|
|
||||||
|
|
||||||
但是使用WAL归档的方式来恢复也有问题,例如查询主库与备库最新的数据记录,发现时间戳差了一秒。也就是说,主库还没有写完的WAL并没有被归档,因此也没有应用。
|
|
||||||
|
|
||||||
```bash
|
|
||||||
[17:37:22] vonng@vonng-mac /var/lib/pgsql
|
|
||||||
$ psql postgres -c 'SELECT max(ts) FROM foobar;'
|
|
||||||
max
|
|
||||||
----------------------------
|
|
||||||
2018-01-25 17:30:40.159684
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
|
|
||||||
[17:37:42] vonng@vonng-mac /var/lib/pgsql
|
|
||||||
$ psql postgres -p 5433 -c 'SELECT max(ts) FROM foobar;'
|
|
||||||
max
|
|
||||||
----------------------------
|
|
||||||
2018-01-25 17:30:39.097167
|
|
||||||
(1 row)
|
|
||||||
```
|
|
||||||
|
|
||||||
通常`archive_command, restore_command`主要用于紧急情况下的恢复,比如主库从库都挂了。因为还没有归档
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 3.6 指定进度
|
|
||||||
|
|
||||||
默认情况下,恢复将会一直恢复到 WAL 日志的末尾。下面的参数可以被用来指定一个更早的停止点。`recovery_target`、`recovery_target_name`、`recovery_target_time`和`recovery_target_xid`四个选项中最多只能使用一个,如果在配置文件中使用了多个,将使用最后一个。
|
|
||||||
|
|
||||||
上面四个恢复目标中,常用的是 `recovery_target_time`,用于指明将系统恢复到什么时间。
|
|
||||||
|
|
||||||
另外几个常用的选项包括:
|
|
||||||
|
|
||||||
- `recovery_target_inclusive` (`boolean`) :是否包括目标点,默认为true
|
|
||||||
- `recovery_target_timeline` (`string`): 指定恢复到一个特定的时间线中。
|
|
||||||
- `recovery_target_action` (`enum`):指定在达到恢复目标时服务器应该立刻采取的动作。
|
|
||||||
- `pause`: 暂停恢复,默认选项,可通过`pg_wal_replay_resume`恢复。
|
|
||||||
- `shutdown`: 自动关闭。
|
|
||||||
- `promote`: 开始接受连接
|
|
||||||
|
|
||||||
例如在`2018-01-25 18:51:20` 创建了一个备份
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ psql postgres -c 'SELECT now();'
|
|
||||||
now
|
|
||||||
------------------------------
|
|
||||||
2018-01-25 18:51:20.34732+08
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
|
|
||||||
[18:51:20] vonng@vonng-mac ~
|
|
||||||
$ pg_basebackup -Fp -Pv -Xs -c fast -D /var/lib/pgsql/bkup
|
|
||||||
pg_basebackup: initiating base backup, waiting for checkpoint to complete
|
|
||||||
pg_basebackup: checkpoint completed
|
|
||||||
pg_basebackup: write-ahead log start point: 0/3000028 on timeline 1
|
|
||||||
pg_basebackup: starting background WAL receiver
|
|
||||||
33007/33007 kB (100%), 1/1 tablespace
|
|
||||||
pg_basebackup: write-ahead log end point: 0/30000F8
|
|
||||||
pg_basebackup: waiting for background process to finish streaming ...
|
|
||||||
pg_basebackup: base backup completed
|
|
||||||
```
|
|
||||||
|
|
||||||
之后运行了两分钟,到了`2018-01-25 18:53:05`我们发现有几条脏数据,于是从备份开始恢复,希望恢复到脏数据出现前一分钟的状态,例如`2018-01-25 18:52`
|
|
||||||
|
|
||||||
可以这样配置
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cat >> /var/lib/pgsql/bkup/recovery.conf <<- 'EOF'
|
|
||||||
restore_command = 'cp /var/lib/pgsql/wal/%f %p'
|
|
||||||
recovery_target_time = '2018-01-25 18:52:30'
|
|
||||||
recovery_target_action = 'promote'
|
|
||||||
EOF
|
|
||||||
```
|
|
||||||
|
|
||||||
当新的数据库实例完成恢复之后,可以看到它的状态确实回到了 18:52分,这正是我们期望的。
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ pg_ctl -D /var/lib/pgsql/bkup -o '-p 5433' start
|
|
||||||
waiting for server to start....2018-01-25 18:56:24.147 CST [13120] LOG: listening on IPv6 address "::1", port 5433
|
|
||||||
2018-01-25 18:56:24.147 CST [13120] LOG: listening on IPv4 address "127.0.0.1", port 5433
|
|
||||||
2018-01-25 18:56:24.148 CST [13120] LOG: listening on Unix socket "/tmp/.s.PGSQL.5433"
|
|
||||||
2018-01-25 18:56:24.162 CST [13121] LOG: database system was interrupted; last known up at 2018-01-25 18:51:22 CST
|
|
||||||
2018-01-25 18:56:24.197 CST [13121] LOG: starting point-in-time recovery to 2018-01-25 18:52:30+08
|
|
||||||
2018-01-25 18:56:24.210 CST [13121] LOG: restored log file "000000010000000000000003" from archive
|
|
||||||
2018-01-25 18:56:24.215 CST [13121] LOG: redo starts at 0/3000028
|
|
||||||
2018-01-25 18:56:24.215 CST [13121] LOG: consistent recovery state reached at 0/30000F8
|
|
||||||
2018-01-25 18:56:24.216 CST [13120] LOG: database system is ready to accept read only connections
|
|
||||||
done
|
|
||||||
server started
|
|
||||||
2018-01-25 18:56:24.228 CST [13121] LOG: restored log file "000000010000000000000004" from archive
|
|
||||||
$ 2018-01-25 18:56:25.034 CST [13121] LOG: restored log file "000000010000000000000005" from archive
|
|
||||||
2018-01-25 18:56:25.853 CST [13121] LOG: restored log file "000000010000000000000006" from archive
|
|
||||||
2018-01-25 18:56:26.235 CST [13121] LOG: recovery stopping before commit of transaction 649, time 2018-01-25 18:52:30.492371+08
|
|
||||||
2018-01-25 18:56:26.235 CST [13121] LOG: redo done at 0/67CFD40
|
|
||||||
2018-01-25 18:56:26.235 CST [13121] LOG: last completed transaction was at log time 2018-01-25 18:52:29.425596+08
|
|
||||||
cp: /var/lib/pgsql/wal/00000002.history: No such file or directory
|
|
||||||
2018-01-25 18:56:26.240 CST [13121] LOG: selected new timeline ID: 2
|
|
||||||
cp: /var/lib/pgsql/wal/00000001.history: No such file or directory
|
|
||||||
2018-01-25 18:56:26.293 CST [13121] LOG: archive recovery complete
|
|
||||||
2018-01-25 18:56:26.401 CST [13120] LOG: database system is ready to accept connections
|
|
||||||
$
|
|
||||||
|
|
||||||
# query new server ,确实回到了18:52分
|
|
||||||
$ psql postgres -p 5433 -c 'SELECT max(ts) FROM foobar;'
|
|
||||||
max
|
|
||||||
----------------------------
|
|
||||||
2018-01-25 18:52:29.413911
|
|
||||||
(1 row)
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3.7 时间线
|
|
||||||
|
|
||||||
每当归档文件恢复完成后,也就是服务器可以开始接受新的查询,写新的WAL的时候。会创建一个新的时间线用来区别新生成的WAL记录。WAL文件名由时间线和日志序号组成,因此新的时间线WAL不会覆盖老时间线的WAL。时间线主要用来解决复杂的恢复操作冲突,例如试想一个场景:刚才恢复到18:52分之后,新的服务器开始不断接受请求:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
psql postgres -c 'CREATE TABLE foobar(ts TIMESTAMP);'
|
|
||||||
for((i=0;i<1000;i++)) do
|
|
||||||
sleep 1 && \
|
|
||||||
psql -p 5433 postgres -c 'INSERT INTO foobar SELECT now() FROM generate_series(1,10000)' && \
|
|
||||||
psql -p 5433 postgres -c 'SELECT pg_current_wal_lsn() as current, pg_current_wal_insert_lsn() as insert, pg_current_wal_flush_lsn() as flush;'
|
|
||||||
done
|
|
||||||
```
|
|
||||||
|
|
||||||
可以看到,WAL归档目录中出现了两个`6`号WAL段文件,如果没有前面的时间线作为区分,WAL就会被覆盖。
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ ls -alh wal
|
|
||||||
total 262160
|
|
||||||
drwxr-xr-x 12 vonng wheel 384B Jan 25 18:59 .
|
|
||||||
drwxr-xr-x 6 vonng wheel 192B Jan 25 18:51 ..
|
|
||||||
-rw------- 1 vonng wheel 16M Jan 25 18:51 000000010000000000000001
|
|
||||||
-rw------- 1 vonng wheel 16M Jan 25 18:51 000000010000000000000002
|
|
||||||
-rw------- 1 vonng wheel 16M Jan 25 18:51 000000010000000000000003
|
|
||||||
-rw------- 1 vonng wheel 302B Jan 25 18:51 000000010000000000000003.00000028.backup
|
|
||||||
-rw------- 1 vonng wheel 16M Jan 25 18:51 000000010000000000000004
|
|
||||||
-rw------- 1 vonng wheel 16M Jan 25 18:52 000000010000000000000005
|
|
||||||
-rw------- 1 vonng wheel 16M Jan 25 18:52 000000010000000000000006
|
|
||||||
-rw------- 1 vonng wheel 50B Jan 25 18:56 00000002.history
|
|
||||||
-rw------- 1 vonng wheel 16M Jan 25 18:58 000000020000000000000006
|
|
||||||
-rw------- 1 vonng wheel 16M Jan 25 18:59 000000020000000000000007
|
|
||||||
```
|
|
||||||
|
|
||||||
假设完成恢复之后又反悔了,则可以用基础备份通过指定`recovery_target_timeline = '1'` 再次恢复回第一次运行到18:53 时的状态。
|
|
||||||
|
|
||||||
### 3.8 其他注意事项
|
|
||||||
|
|
||||||
* 在Pg 10之前,哈希索引上的操作不会被记录在WAL中,需要在Slave上手工REINDEX。
|
|
||||||
* 不要在创建基础备份的时候修改任何**模板数据库**
|
|
||||||
* 注意表空间会严格按照字面值记录其路径,如果使用了表空间,恢复时要非常小心。
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 4. 制作备机
|
|
||||||
|
|
||||||
通过主从(master-slave),可以同时提高可用性与可靠性。
|
|
||||||
|
|
||||||
- 主从读写分离提高性能:写请求落在Master上,通过WAL流复制传输到从库上,从库接受读请求。
|
|
||||||
- 通过备份提高可靠性:当一台服务器故障时,可以立即由另一台顶上(promote slave or & make new slave)
|
|
||||||
|
|
||||||
通常主从、副本、备机这些属于高可用的话题。但从另一个角度来讲,备机也是备份的一种。
|
|
||||||
|
|
||||||
#### 创建目录
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo mkdir /var/lib/pgsql && sudo chown postgres:postgres /var/lib/pgsql/
|
|
||||||
mkdir -p /var/lib/pgsql/master /var/lib/pgsql/slave /var/lib/pgsql/wal
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 制作主库
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pg_ctl -D /var/lib/pgsql/master init && pg_ctl -D /var/lib/pgsql/master start
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 创建用户
|
|
||||||
|
|
||||||
创建备库需要一个具有`REPLICATION`权限的用户,这里在Master中创建`replication`用户
|
|
||||||
|
|
||||||
```bash
|
|
||||||
psql postgres -c 'CREATE USER replication REPLICATION;'
|
|
||||||
```
|
|
||||||
|
|
||||||
为了创建从库,需要一个具有`REPLICATION`权限的用户,并在`pg_hba`中允许访问,10中默认允许:
|
|
||||||
|
|
||||||
```ini
|
|
||||||
local replication all trust
|
|
||||||
host replication all 127.0.0.1/32 trust
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 制作备库
|
|
||||||
|
|
||||||
通过`pg_basebackup`创建一个slave实例。实际上是连接到Master实例,并复制一份数据目录到本地。
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pg_basebackup -Fp -Pv -R -c fast -U replication -h localhost -D /var/lib/pgsql/slave
|
|
||||||
```
|
|
||||||
|
|
||||||
这里的关键是通过`-R` 选项,在备份的制作过程中自动将主机的连接信息填入`recovery.conf`,这样使用`pg_ctl `启动时,数据库会意识到自己是备机,并从主机自动拉取WAL追赶进度。
|
|
||||||
|
|
||||||
#### 启动从库
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pg_ctl -D /var/lib/pgsql/slave -o "-p 5433" start
|
|
||||||
```
|
|
||||||
|
|
||||||
从库与主库的唯一区别在于,数据目录中多了一个`recovery.conf`文件。这个文件不仅仅可以用于标识从库的身份,而且在故障恢复时也需要用到。对于`pg_basebackup`构造的从库,它默认包含两个参数:
|
|
||||||
|
|
||||||
```ini
|
|
||||||
standby_mode = 'on'
|
|
||||||
primary_conninfo = 'user=replication passfile=''/Users/vonng/.pgpass'' host=localhost port=5432 sslmode=prefer sslcompression=1 krbsrvname=postgres target_session_attrs=any'
|
|
||||||
```
|
|
||||||
|
|
||||||
`standby_mode`指明是否将PostgreSQL作为从库启动。
|
|
||||||
|
|
||||||
在备份时,`standby_mode`默认关闭,这样当所有的WAL拉取完毕后,就完成恢复,进入正常工作模式。
|
|
||||||
|
|
||||||
如果打开,那么数据库会意识到自己是备机,那么即使到达WAL末尾也不会停止,它会持续拉取主库的WAL,追赶主库的进度。
|
|
||||||
|
|
||||||
拉取WAL有两种办法,通过`primary_conninfo`流式复制拉取(9.0后的新特性,推荐,默认),或者通过`restore_command`来手工指明WAL的获取方式(老办法,恢复时使用)。
|
|
||||||
|
|
||||||
#### 查看状态
|
|
||||||
|
|
||||||
主库的所有从库可以通过系统视图`pg_stat_replication`查阅:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ psql postgres -tzxc 'SELECT * FROM pg_stat_replication;'
|
|
||||||
pid | 1947
|
|
||||||
usesysid | 16384
|
|
||||||
usename | replication
|
|
||||||
application_name | walreceiver
|
|
||||||
client_addr | ::1
|
|
||||||
client_hostname |
|
|
||||||
client_port | 54124
|
|
||||||
backend_start | 2018-01-25 13:24:57.029203+08
|
|
||||||
backend_xmin |
|
|
||||||
state | streaming
|
|
||||||
sent_lsn | 0/5017F88
|
|
||||||
write_lsn | 0/5017F88
|
|
||||||
flush_lsn | 0/5017F88
|
|
||||||
replay_lsn | 0/5017F88
|
|
||||||
write_lag |
|
|
||||||
flush_lag |
|
|
||||||
replay_lag |
|
|
||||||
sync_priority | 0
|
|
||||||
sync_state | async
|
|
||||||
```
|
|
||||||
|
|
||||||
检查主库和备库的状态可以使用函数`pg_is_in_recovery`,备库会处于恢复状态:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ psql postgres -Atzc 'SELECT pg_is_in_recovery()' && \
|
|
||||||
psql postgres -p 5433 -Atzc 'SELECT pg_is_in_recovery()'
|
|
||||||
f
|
|
||||||
t
|
|
||||||
```
|
|
||||||
|
|
||||||
在主库中建表,从库也能看到。
|
|
||||||
|
|
||||||
```bash
|
|
||||||
psql postgres -c 'CREATE TABLE foobar(i INTEGER);' && psql postgres -p 5433 -c '\d'
|
|
||||||
```
|
|
||||||
|
|
||||||
在主库中插入数据,从库也能看到
|
|
||||||
|
|
||||||
```bash
|
|
||||||
psql postgres -c 'INSERT INTO foobar VALUES (1);' && \
|
|
||||||
psql postgres -p 5433 -c 'SELECT * FROM foobar;'
|
|
||||||
```
|
|
||||||
|
|
||||||
现在主备已经配置就绪
|
|
@ -1,154 +0,0 @@
|
|||||||
# Server Configuration
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 参数配置
|
|
||||||
|
|
||||||
* 查看配置的方法:
|
|
||||||
* 查看数据库目录下配置文件
|
|
||||||
* 系统视图:`SELECT name,setting FROM pg_settings where name ~ 'xxx'';`
|
|
||||||
* 系统函数:`current_setting(setting_name [, missing_ok ])`
|
|
||||||
* SQL语句:`SHOW <name> | ALL;`
|
|
||||||
* 修改配置的方法:
|
|
||||||
* 系统级修改:修改配置文件、执行`ALTER SYSTEM xxx`、启动时`-c`参数。
|
|
||||||
* 数据库级别: `ALTER DATABASE`
|
|
||||||
* 会话级别:通过SET或`set_config(setting_name, new_value, false)`,更新pg_settings视图
|
|
||||||
* 事务级别:通过SET或`set_config(setting_name, new_value, true)`
|
|
||||||
|
|
||||||
* 生效配置的方法:
|
|
||||||
* 系统管理函数:`SELECT pg_reload_conf()`
|
|
||||||
* `pg_ctl reload`,或发送`SIGHUP`
|
|
||||||
* `/etc/init.d/postgresql-x.x reload`(el6)
|
|
||||||
* `systemctl reload service.postgresql-9.x` (el7)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 权限配置
|
|
||||||
|
|
||||||
* 在`postgresql.conf`中配置`listen_addresses`为`*`以允许外部连接。
|
|
||||||
* 在`pg_hba.conf`中配置访问权限。hba是`Host based authentication`
|
|
||||||
* `pg_hba`的配置项为`<type,database,user,address,method>`构成的五元组,指明了:
|
|
||||||
* 什么样的连接方式:`local, host, hostssl, hostnossl`
|
|
||||||
* 什么样的数据库:`all, sameuser, samerole, replication, <dbname>`
|
|
||||||
* 什么样的用户:`all, <username>, +<groupname>`
|
|
||||||
* 什么样的地址:IP地址,CIDR地址,`0.0.0.0/0`表示所有机器。
|
|
||||||
* 什么样的行为:`trust, reject, md5, password, ident, peer...`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 内核优化参数
|
|
||||||
|
|
||||||
```bash
|
|
||||||
optimize() {
|
|
||||||
if (( "$#" == 1 )); then
|
|
||||||
local datadir="$1"; shift
|
|
||||||
local mem="$(free \
|
|
||||||
| awk '/Mem:/{print $2}')"
|
|
||||||
local swap="$(free \
|
|
||||||
| awk '/Swap:/{print $2}')"
|
|
||||||
|
|
||||||
cat > /etc/sysctl.conf <<- EOF
|
|
||||||
# Database kernel optimisation
|
|
||||||
fs.aio-max-nr = 1048576
|
|
||||||
fs.file-max = 76724600
|
|
||||||
kernel.sem = 4096 2147483647 2147483646 512000
|
|
||||||
kernel.shmmax = $(( $mem * 1024 / 2 ))
|
|
||||||
kernel.shmall = $(( $mem / 5 ))
|
|
||||||
kernel.shmmni = 819200
|
|
||||||
net.core.netdev_max_backlog = 10000
|
|
||||||
net.core.rmem_default = 262144
|
|
||||||
net.core.rmem_max = 4194304
|
|
||||||
net.core.wmem_default = 262144
|
|
||||||
net.core.wmem_max = 4194304
|
|
||||||
net.core.somaxconn = 4096
|
|
||||||
net.ipv4.tcp_max_syn_backlog = 4096
|
|
||||||
net.ipv4.tcp_keepalive_intvl = 20
|
|
||||||
net.ipv4.tcp_keepalive_probes = 3
|
|
||||||
net.ipv4.tcp_keepalive_time = 60
|
|
||||||
net.ipv4.tcp_mem = 8388608 12582912 16777216
|
|
||||||
net.ipv4.tcp_fin_timeout = 5
|
|
||||||
net.ipv4.tcp_synack_retries = 2
|
|
||||||
net.ipv4.tcp_syncookies = 1
|
|
||||||
net.ipv4.tcp_timestamps = 1
|
|
||||||
net.ipv4.tcp_tw_recycle = 0
|
|
||||||
net.ipv4.tcp_tw_reuse = 1
|
|
||||||
net.ipv4.tcp_max_tw_buckets = 262144
|
|
||||||
net.ipv4.tcp_rmem = 8192 87380 16777216
|
|
||||||
net.ipv4.tcp_wmem = 8192 65536 16777216
|
|
||||||
vm.dirty_background_bytes = 409600000
|
|
||||||
net.ipv4.ip_local_port_range = 40000 65535
|
|
||||||
vm.dirty_expire_centisecs = 6000
|
|
||||||
vm.dirty_ratio = 80
|
|
||||||
vm.dirty_writeback_centisecs = 50
|
|
||||||
vm.extra_free_kbytes = 4096000
|
|
||||||
vm.min_free_kbytes = 2097152
|
|
||||||
vm.mmap_min_addr = 65536
|
|
||||||
vm.swappiness = 0
|
|
||||||
vm.overcommit_memory = 2
|
|
||||||
vm.overcommit_ratio = $(( ( $mem - $swap ) * 100 / $mem ))
|
|
||||||
vm.zone_reclaim_mode = 0
|
|
||||||
EOF
|
|
||||||
sysctl -p
|
|
||||||
|
|
||||||
if ( ! type -f grubby &>/dev/null ); then
|
|
||||||
yum install -q -y grubby
|
|
||||||
fi
|
|
||||||
grubby --update-kernel=/boot/vmlinuz-$(uname -r) --args="numa=off transparent_hugepage=never"
|
|
||||||
|
|
||||||
if [[ -x /opt/MegaRAID/MegaCli/MegaCli64 ]]; then
|
|
||||||
/opt/MegaRAID/MegaCli/MegaCli64 -LDSetProp WB -LALL -aALL
|
|
||||||
/opt/MegaRAID/MegaCli/MegaCli64 -LDSetProp ADRA -LALL -aALL
|
|
||||||
/opt/MegaRAID/MegaCli/MegaCli64 -LDSetProp -DisDskCache -LALL -aALL
|
|
||||||
/opt/MegaRAID/MegaCli/MegaCli64 -LDSetProp -Cached -LALL -aALL
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ( ! grep -q 'Database optimisation' /etc/rc.local ); then
|
|
||||||
cat >> /etc/rc.local <<- EOF
|
|
||||||
# Database optimisation
|
|
||||||
echo 'never' > /sys/kernel/mm/transparent_hugepage/enabled
|
|
||||||
echo 'never' > /sys/kernel/mm/transparent_hugepage/defrag
|
|
||||||
#blockdev --setra 16384 $(echo $(blkid | awk -F':' '$1!~"block"{print $1}'))
|
|
||||||
EOF
|
|
||||||
chmod +x /etc/rc.d/rc.local
|
|
||||||
fi
|
|
||||||
|
|
||||||
cat > /etc/security/limits.d/postgresql.conf <<- EOF
|
|
||||||
postgres soft nproc 655360
|
|
||||||
postgres hard nproc 655360
|
|
||||||
postgres hard nofile 655360
|
|
||||||
postgres soft nofile 655360
|
|
||||||
postgres soft stack unlimited
|
|
||||||
postgres hard stack unlimited
|
|
||||||
postgres soft core unlimited
|
|
||||||
postgres hard core unlimited
|
|
||||||
postgres soft memlock 250000000
|
|
||||||
postgres hard memlock 250000000
|
|
||||||
EOF
|
|
||||||
cat > /etc/security/limits.d/pgbouncer.conf <<- EOF
|
|
||||||
pgbouncer soft nproc 655360
|
|
||||||
pgbouncer hard nofile 655360
|
|
||||||
pgbouncer soft nofile 655360
|
|
||||||
pgbouncer soft stack unlimited
|
|
||||||
pgbouncer hard stack unlimited
|
|
||||||
pgbouncer soft core unlimited
|
|
||||||
pgbouncer hard core unlimited
|
|
||||||
pgbouncer soft memlock 250000000
|
|
||||||
pgbouncer hard memlock 250000000
|
|
||||||
EOF
|
|
||||||
cat > /etc/security/limits.d/pgpool.conf <<- EOF
|
|
||||||
pgpool soft nproc 655360
|
|
||||||
pgpool hard nofile 655360
|
|
||||||
pgpool soft nofile 655360
|
|
||||||
pgpool soft stack unlimited
|
|
||||||
pgpool hard stack unlimited
|
|
||||||
pgpool soft core unlimited
|
|
||||||
pgpool hard core unlimited
|
|
||||||
pgpool soft memlock 250000000
|
|
||||||
pgpool hard memlock 250000000
|
|
||||||
EOF
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
@ -1,207 +0,0 @@
|
|||||||
---
|
|
||||||
author: "Vonng"
|
|
||||||
description: "PostgreSQL 高可用"
|
|
||||||
categories: ["DBA"]
|
|
||||||
tags: ["PostgreSQL","HA"]
|
|
||||||
type: "post"
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# PostgreSQL高可用
|
|
||||||
|
|
||||||
可以使多个数据库服务器协同工作,从而实现:
|
|
||||||
|
|
||||||
* 高可用性(HA):若主服务器(master)失效,则允许从服务器(slave)快速接手它的任务
|
|
||||||
|
|
||||||
* 负载均衡(LB):允许多个服务器提供相同的数据
|
|
||||||
|
|
||||||
只读的数据库服务器可以容易地协同服务。不幸的是,大部分数据库服务器收到的请求是读/写混合的,难以组合。 只读数据只需要在每台服务器上放置一次, 但对任意服务器的一次写动作,却必须被传播给所有的服务器, 才能保证一致性。
|
|
||||||
|
|
||||||
这种同步问题是服务器一起工作的最根本的困难。 因为没有单一解决方案能够消除该同步问题对所有用例的影响。有多种解决方案, 每一种方案都以一种不同的方式提出了这个问题, 并且对于一种特定的负载最小化了该问题所产生的影响。
|
|
||||||
|
|
||||||
某些方案通过只允许一台服务器修改数据来处理同步。 能修改数据的服务器被称为读/写、*主控*或*主要*服务器。 跟踪主控机中改变的服务器被称为*后备*或*从属*服务器。 如果一台后备服务器只有被提升为一台主控服务器后才能被连接, 它被称为一台*温后备*服务器, 而一台能够接受连接并且提供只读查询的后备服务器被称为一台 *热后备*服务器。
|
|
||||||
|
|
||||||
某些方案是同步的, 即一个数据修改事务只有到所有服务器都提交了该事务之后才被认为是提交成功。 这保证了一次故障转移不会丢失任何数据并且所有负载均衡的服务器将返回一致的结果 (不管哪台服务器被查询)。相反, 异步的方案允许在一次提交和它被传播到其他服务器之间有一些延迟, 这产生了切换到一个备份服务器时丢失某些事务的可能性, 并且负载均衡的服务器可能会返回略微陈旧的结果。当同步通信可能很慢时, 可以使用异步通信。
|
|
||||||
|
|
||||||
方案也可以按照它们的粒度进行分类。某些方案只能处理一整个数据库服务器, 而其他的允许在每个表或每个数据库的级别上进行控制。
|
|
||||||
|
|
||||||
在任何选择中,都必须考虑性能。通常在功能和性能之间都存在着权衡。例如, 在一个低速网络上的一种完全同步的方案可能使性能减少超过一半, 而一种异步的方案产生的性能影响可能是最小的。
|
|
||||||
|
|
||||||
本节的剩余部分列出了多种故障转移、复制和负载均衡方案。其中也有一个[术语表](http://www.postgres-r.org/documentation/terms)可用。
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
通过主从(master-slave),可以同时提高可用性与可靠性。
|
|
||||||
|
|
||||||
- 主从读写分离提高性能:写请求落在Master上,通过WAL流复制传输到从库上,从库接受读请求。
|
|
||||||
- 通过备份提高可靠性:当一台服务器故障时,可以立即由另一台顶上(promote slave or & make new slave)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 准备环境
|
|
||||||
|
|
||||||
### 创建目录
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo mkdir /var/lib/pgsql && sudo chown postgres:postgres /var/lib/pgsql/
|
|
||||||
mkdir -p /var/lib/pgsql/master /var/lib/pgsql/slave /var/lib/pgsql/wal
|
|
||||||
```
|
|
||||||
|
|
||||||
### 制作主库
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pg_ctl -D /var/lib/pgsql/master init && pg_ctl -D /var/lib/pgsql/master start
|
|
||||||
```
|
|
||||||
|
|
||||||
### 创建用户
|
|
||||||
|
|
||||||
创建备库需要一个具有`REPLICATION`权限的用户,这里在Master中创建`replication`用户
|
|
||||||
|
|
||||||
```bash
|
|
||||||
psql postgres -c 'CREATE USER replication REPLICATION;'
|
|
||||||
```
|
|
||||||
|
|
||||||
为了创建从库,需要一个具有`REPLICATION`权限的用户,并在`pg_hba`中允许访问,10中默认允许:
|
|
||||||
|
|
||||||
```ini
|
|
||||||
local replication all trust
|
|
||||||
host replication all 127.0.0.1/32 trust
|
|
||||||
```
|
|
||||||
|
|
||||||
现在我们已经有了一个可用的主库。
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 制作备库
|
|
||||||
|
|
||||||
制作备库分为两步:
|
|
||||||
|
|
||||||
- 有一个基础备份:拷贝数据目录,`pg_basebackup`
|
|
||||||
- 应用/追赶更新:拷贝并应用WAL,`primary_conninfo` 与`restore_command`
|
|
||||||
|
|
||||||
### 拷贝数据库目录
|
|
||||||
|
|
||||||
通过`pg_basebackup`创建一个基础备份。基础备份实际上就是主库数据目录的一份物理拷贝,既可以放在安全的地方用于备用,也可以跑起来作为一个备库,分担主库的写压力,并提供冗余以提高可靠性。
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pg_basebackup -Fp -Pv -R -c fast -U replication -h localhost -D /var/lib/pgsql/slave
|
|
||||||
```
|
|
||||||
|
|
||||||
启动从库,使用5433端口
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pg_ctl -D /var/lib/pgsql/slave -o "-p 5433" start
|
|
||||||
```
|
|
||||||
|
|
||||||
从库与主库的唯一区别在于,数据目录中多了一个`recovery.conf`文件。这个文件不仅仅可以用于标识从库的身份,而且在故障恢复时也需要用到。对于`pg_basebackup`构造的从库,它默认包含两个参数:
|
|
||||||
|
|
||||||
```ini
|
|
||||||
standby_mode = 'on'
|
|
||||||
primary_conninfo = 'user=replication passfile=''/Users/vonng/.pgpass'' host=localhost port=5432 sslmode=prefer sslcompression=1 krbsrvname=postgres target_session_attrs=any'
|
|
||||||
```
|
|
||||||
|
|
||||||
`standby_mode`指明是否将PostgreSQL作为从库启动。如果打开,那么从库会持续拉取WAL,即使已经到了WAL的最后位置也不会停止,实时保持和主库的同步。当制作备库时,需要设置`standby_mode=on`
|
|
||||||
|
|
||||||
与此同时,还需要在`recovery.conf`中指明如何获取WAL。有两种方式:
|
|
||||||
|
|
||||||
- 流式获取:通过`primary_conninfo`从一个主库流式拉取。这是9.0后的新特性,流式复制提供了更好的实时性,也更加便于使用。
|
|
||||||
- 恢复命令:通过`restore_command`从指定位置不断复制并应用WAL日志,这是传统的方式,但实时性稍差(始终落后主库一个没写完的WAL文件)。但在主库挂了无法访问时,这种方式可以用于从归档的WAL中恢复。
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 检查状态
|
|
||||||
|
|
||||||
主库的所有从库可以通过系统视图`pg_stat_replication`查阅:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ psql postgres -tzxc 'SELECT * FROM pg_stat_replication;'
|
|
||||||
pid | 1947
|
|
||||||
usesysid | 16384
|
|
||||||
usename | replication
|
|
||||||
application_name | walreceiver
|
|
||||||
client_addr | ::1
|
|
||||||
client_hostname |
|
|
||||||
client_port | 54124
|
|
||||||
backend_start | 2018-01-25 13:24:57.029203+08
|
|
||||||
backend_xmin |
|
|
||||||
state | streaming
|
|
||||||
sent_lsn | 0/5017F88
|
|
||||||
write_lsn | 0/5017F88
|
|
||||||
flush_lsn | 0/5017F88
|
|
||||||
replay_lsn | 0/5017F88
|
|
||||||
write_lag |
|
|
||||||
flush_lag |
|
|
||||||
replay_lag |
|
|
||||||
sync_priority | 0
|
|
||||||
sync_state | async
|
|
||||||
```
|
|
||||||
|
|
||||||
检查主库和备库的状态可以使用函数`pg_is_in_recovery`,备库会处于恢复状态:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ psql postgres -Atzc 'SELECT pg_is_in_recovery()' && \
|
|
||||||
psql postgres -p 5433 -Atzc 'SELECT pg_is_in_recovery()'
|
|
||||||
f
|
|
||||||
t
|
|
||||||
```
|
|
||||||
|
|
||||||
在主库中建表,从库也能看到。
|
|
||||||
|
|
||||||
```bash
|
|
||||||
psql postgres -c 'CREATE TABLE foobar(i INTEGER);' && psql postgres -p 5433 -c '\d'
|
|
||||||
```
|
|
||||||
|
|
||||||
在主库中插入数据,从库也能看到
|
|
||||||
|
|
||||||
```bash
|
|
||||||
psql postgres -c 'INSERT INTO foobar VALUES (1);' && \
|
|
||||||
psql postgres -p 5433 -c 'SELECT * FROM foobar;'
|
|
||||||
```
|
|
||||||
|
|
||||||
现在主备已经配置就绪
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 制作备份
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 灾难恢复
|
|
||||||
|
|
||||||
在使用主备时,按照严重程度,灾难恢复分为三种情况:
|
|
||||||
|
|
||||||
- 从库故障:重新制作一个从库。
|
|
||||||
- 主库故障:晋升(promote)从库为主库,并通知应用方或修改连接池配置,可能导致短暂的不可用状态。
|
|
||||||
- 主从都故障:利用基础备份和归档WAL制作新的主从。在此期间数据库不可用。
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 从库故障:制作备库
|
|
||||||
|
|
||||||
通常从库承载着只读流量,当从库故障时,读请求可以重新路由回主库。
|
|
||||||
|
|
||||||
但负载很大时,从库故障可能进一步导致主库压力过大发生故障,因此需要及时制作一个新的备库并启用。
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 主库故障:晋升从库
|
|
||||||
|
|
||||||
- 晋升主库为从库:`pg_ctl promote`
|
|
||||||
- 修复主库,将修复后的主库作为新的从库:`pg_rewind`
|
|
||||||
- 如果主库无法修复,可以立即制作新的从库:`pg_basebackup`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 主从故障
|
|
||||||
|
|
||||||
当主从都故障时,数据库进入不可用状态。可以通过基础备份与归档的WAL制作新的实例。
|
|
||||||
|
|
@ -1,924 +0,0 @@
|
|||||||
---
|
|
||||||
author: "Vonng"
|
|
||||||
description: "PostgreSQL安装方法"
|
|
||||||
categories: ["Dev"]
|
|
||||||
tags: ["PostgreSQL","Admin", "Install"]
|
|
||||||
type: "post"
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# PostgreSQL Installation
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 生产环境使用的方式
|
|
||||||
|
|
||||||
使用`pg_init.sh`脚本
|
|
||||||
|
|
||||||
通过`-P` 参数指定数据库角色名,`-V`指定数据库版本号,`-R`指定数据库的角色(master,slave)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
#!/bin/env bash
|
|
||||||
|
|
||||||
# ########################################################################
|
|
||||||
# PostgreSQL environment initialize program
|
|
||||||
# License: DBA
|
|
||||||
# Version: 1.1
|
|
||||||
# Authors: panwenhang
|
|
||||||
# ########################################################################
|
|
||||||
|
|
||||||
declare -r PROGDIR="$(cd $(dirname $0) && pwd)"
|
|
||||||
declare -r PROGNAME="$(basename $0)"
|
|
||||||
|
|
||||||
declare -x -r DIR_BASE='/export'
|
|
||||||
declare -x -r HOME='/home'
|
|
||||||
|
|
||||||
export PATH=/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:$PATH
|
|
||||||
|
|
||||||
# ##########################################################
|
|
||||||
# usage of this script
|
|
||||||
# ##########################################################
|
|
||||||
usage() {
|
|
||||||
cat <<- EOF
|
|
||||||
|
|
||||||
Usage: $PROGNAME [options]
|
|
||||||
|
|
||||||
You must execute this program with system superuser privilege (root)!
|
|
||||||
The product and dbversion must use together.
|
|
||||||
|
|
||||||
OPTIONS:
|
|
||||||
-U, --superuser=username Database superuser
|
|
||||||
-P, --product=product product name
|
|
||||||
-B, --dbbase=dbbase base directory of postgresql
|
|
||||||
-R, --role=master master or slave
|
|
||||||
-V, --dbversion Database version
|
|
||||||
-h, --help usage of this program
|
|
||||||
|
|
||||||
Example:
|
|
||||||
$PROGNAME -P test -V 9.6
|
|
||||||
$PROGNAME -B /var/lib/pgsql/9.6 -V 9.6
|
|
||||||
$PROGNAME -U dbsu -P test -R master -V 9.6
|
|
||||||
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
# ##########################################################
|
|
||||||
# check execute user
|
|
||||||
# ##########################################################
|
|
||||||
check_exec_user() {
|
|
||||||
if [[ "$(whoami)" != "root" ]]; then
|
|
||||||
usage
|
|
||||||
exit -1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# ##########################################################
|
|
||||||
# initialize superuser
|
|
||||||
# args:
|
|
||||||
# arg 1: superuser name
|
|
||||||
# ##########################################################
|
|
||||||
user_init() {
|
|
||||||
if (( "$#" == 1 )); then
|
|
||||||
local dbsu="$1"; shift
|
|
||||||
|
|
||||||
if ( ! grep -q "$dbsu" /etc/group ); then
|
|
||||||
groupadd "$dbsu"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ( ! grep -q "$dbsu" /etc/passwd ); then
|
|
||||||
useradd -d "$HOME"/"$dbsu" -g "$dbsu" "$dbsu"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ( ! grep -q "$dbsu" /etc/passwd ) && ( ! grep -q "$dbsu" /etc/sudoers ); then
|
|
||||||
chmod u+w /etc/sudoers
|
|
||||||
echo "$dbsu ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
|
|
||||||
fi
|
|
||||||
|
|
||||||
return 0
|
|
||||||
else
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# ##########################################################
|
|
||||||
# initialize directory
|
|
||||||
# args:
|
|
||||||
# arg 1: superuser name
|
|
||||||
# arg 2: databse base directory
|
|
||||||
# ##########################################################
|
|
||||||
dir_init() {
|
|
||||||
if (( "$#" == 2 )); then
|
|
||||||
local dbsu="$1"; shift
|
|
||||||
local datadir="$1"; shift
|
|
||||||
|
|
||||||
mkdir -p "$datadir"/{data,backup,rbackup,arcxlog,conf,scripts}
|
|
||||||
chown -R "$dbsu":"$dbsu" "$datadir"
|
|
||||||
chmod 0700 "$datadir"/data
|
|
||||||
|
|
||||||
return 0
|
|
||||||
else
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# ##########################################################
|
|
||||||
# install package of postgresql
|
|
||||||
# args:
|
|
||||||
# arg 1: postgresql version
|
|
||||||
# ##########################################################
|
|
||||||
pg_install() {
|
|
||||||
if (( "$#" == 1 )); then
|
|
||||||
local dbversion="$1"; shift
|
|
||||||
local major_version="${dbversion:0:3}"
|
|
||||||
local short_version="$(echo $dbversion \
|
|
||||||
| awk -F'.' '{print $1$2}')"
|
|
||||||
local rpm_base=''
|
|
||||||
local os_release=''
|
|
||||||
|
|
||||||
if ( grep -q 'CentOS release 6' /etc/redhat-release ); then
|
|
||||||
rpm_base="http://yum.postgresql.org/$major_version/redhat/rhel-6Server-$(uname -m)"
|
|
||||||
os_release="rhel6"
|
|
||||||
elif ( grep -q 'CentOS Linux release 7' /etc/redhat-release ); then
|
|
||||||
rpm_base="http://yum.postgresql.org/$major_version/redhat/rhel-7Server-$(uname -m)"
|
|
||||||
os_release="rhel7"
|
|
||||||
fi
|
|
||||||
|
|
||||||
yum clean all && yum install -q -y epel-release
|
|
||||||
yum install -q -y tcl perl-ExtUtils-Embed libxml2 libxslt uuid readline lz4 nc
|
|
||||||
yum install -q -y "$rpm_base"/pgdg-centos"$short_version"-"$major_version"-1.noarch.rpm
|
|
||||||
yum install -q -y "$rpm_base"/pgdg-centos"$short_version"-"$major_version"-2.noarch.rpm
|
|
||||||
yum install -q -y "$rpm_base"/pgdg-centos"$short_version"-"$major_version"-3.noarch.rpm
|
|
||||||
|
|
||||||
yum install -q -y postgresql"$short_version" postgresql"$short_version"-libs postgresql"$short_version"-server postgresql"$short_version"-contrib postgresql"$short_version"-devel postgresql"$short_version"-debuginfo
|
|
||||||
|
|
||||||
yum install -q -y pgbouncer pgpool-II-"$short_version" pg_top"$short_version" postgis2_"$short_version" postgis2_"$short_version"-client pg_repack"$short_version"
|
|
||||||
|
|
||||||
rm -f /usr/pgsql
|
|
||||||
ln -sf /usr/pgsql-"$major_version" /usr/pgsql
|
|
||||||
echo 'export PATH=/usr/pgsql/bin:$PATH' > /etc/profile.d/pgsql.sh
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# ##########################################################
|
|
||||||
# install package of postgresql for custom
|
|
||||||
# args:
|
|
||||||
# arg 1: postgresql version
|
|
||||||
# ##########################################################
|
|
||||||
pg_install_custom() {
|
|
||||||
if (( "$#" == 1 )); then
|
|
||||||
local dbversion="$1"; shift
|
|
||||||
local major_version="${dbversion:0:3}"
|
|
||||||
local short_version="$(echo $dbversion \
|
|
||||||
| awk -F'.' '{print $1$2}')"
|
|
||||||
local rpm_base='http://download.postgresql.com/packages/RPMS/'
|
|
||||||
local os_release=''
|
|
||||||
|
|
||||||
if ( grep -q 'CentOS release 6' /etc/redhat-release ); then
|
|
||||||
os_release="el6"
|
|
||||||
elif ( grep -q 'CentOS Linux release 7' /etc/redhat-release ); then
|
|
||||||
os_release="el7"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ( ! rpm --quiet -q postgresql-"$dbversion"-1."$os_release"."$(uname -m)" ); then
|
|
||||||
yum install -q -y tcl perl-ExtUtils-Embed libxml2 libxslt uuid readline
|
|
||||||
rpm -ivh --force "$rpm_base"/"$(uname -m)"/postgresql-"$dbversion"-1."$os_release"."$(uname -m)".rpm
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# ##########################################################
|
|
||||||
# postgresql shared xlog archive directory
|
|
||||||
# args:
|
|
||||||
# arg 1: postgresql base directory
|
|
||||||
# ##########################################################
|
|
||||||
shared_xlog() {
|
|
||||||
if (( "$#" == 1 )); then
|
|
||||||
local datadir="$1"; shift
|
|
||||||
yum install -y -q nfs-utils
|
|
||||||
echo "$datadir/arcxlog 10.191.0.0/16(rw)" > /etc/exports
|
|
||||||
service nfs start
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# ##########################################################
|
|
||||||
# postgresql optimize
|
|
||||||
# args:
|
|
||||||
# arg 1: postgresql base directory
|
|
||||||
# ##########################################################
|
|
||||||
optimize() {
|
|
||||||
if (( "$#" == 1 )); then
|
|
||||||
local datadir="$1"; shift
|
|
||||||
local mem="$(free \
|
|
||||||
| awk '/Mem:/{print $2}')"
|
|
||||||
local swap="$(free \
|
|
||||||
| awk '/Swap:/{print $2}')"
|
|
||||||
|
|
||||||
cat > /etc/sysctl.conf <<- EOF
|
|
||||||
# Database kernel optimisation
|
|
||||||
fs.aio-max-nr = 1048576
|
|
||||||
fs.file-max = 76724600
|
|
||||||
kernel.sem = 4096 2147483647 2147483646 512000
|
|
||||||
kernel.shmmax = $(( $mem * 1024 / 2 ))
|
|
||||||
kernel.shmall = $(( $mem / 5 ))
|
|
||||||
kernel.shmmni = 819200
|
|
||||||
net.core.netdev_max_backlog = 10000
|
|
||||||
net.core.rmem_default = 262144
|
|
||||||
net.core.rmem_max = 4194304
|
|
||||||
net.core.wmem_default = 262144
|
|
||||||
net.core.wmem_max = 4194304
|
|
||||||
net.core.somaxconn = 4096
|
|
||||||
net.ipv4.tcp_max_syn_backlog = 4096
|
|
||||||
net.ipv4.tcp_keepalive_intvl = 20
|
|
||||||
net.ipv4.tcp_keepalive_probes = 3
|
|
||||||
net.ipv4.tcp_keepalive_time = 60
|
|
||||||
net.ipv4.tcp_mem = 8388608 12582912 16777216
|
|
||||||
net.ipv4.tcp_fin_timeout = 5
|
|
||||||
net.ipv4.tcp_synack_retries = 2
|
|
||||||
net.ipv4.tcp_syncookies = 1
|
|
||||||
net.ipv4.tcp_timestamps = 1
|
|
||||||
net.ipv4.tcp_tw_recycle = 0
|
|
||||||
net.ipv4.tcp_tw_reuse = 1
|
|
||||||
net.ipv4.tcp_max_tw_buckets = 262144
|
|
||||||
net.ipv4.tcp_rmem = 8192 87380 16777216
|
|
||||||
net.ipv4.tcp_wmem = 8192 65536 16777216
|
|
||||||
vm.dirty_background_bytes = 409600000
|
|
||||||
net.ipv4.ip_local_port_range = 40000 65535
|
|
||||||
vm.dirty_expire_centisecs = 6000
|
|
||||||
vm.dirty_ratio = 80
|
|
||||||
vm.dirty_writeback_centisecs = 50
|
|
||||||
vm.extra_free_kbytes = 4096000
|
|
||||||
vm.min_free_kbytes = 2097152
|
|
||||||
vm.mmap_min_addr = 65536
|
|
||||||
vm.swappiness = 0
|
|
||||||
vm.overcommit_memory = 2
|
|
||||||
vm.overcommit_ratio = $(( ( $mem - $swap ) * 100 / $mem ))
|
|
||||||
vm.zone_reclaim_mode = 0
|
|
||||||
EOF
|
|
||||||
sysctl -p
|
|
||||||
|
|
||||||
if ( ! type -f grubby &>/dev/null ); then
|
|
||||||
yum install -q -y grubby
|
|
||||||
fi
|
|
||||||
grubby --update-kernel=/boot/vmlinuz-$(uname -r) --args="numa=off transparent_hugepage=never"
|
|
||||||
|
|
||||||
if [[ -x /opt/MegaRAID/MegaCli/MegaCli64 ]]; then
|
|
||||||
/opt/MegaRAID/MegaCli/MegaCli64 -LDSetProp WB -LALL -aALL
|
|
||||||
/opt/MegaRAID/MegaCli/MegaCli64 -LDSetProp ADRA -LALL -aALL
|
|
||||||
/opt/MegaRAID/MegaCli/MegaCli64 -LDSetProp -DisDskCache -LALL -aALL
|
|
||||||
/opt/MegaRAID/MegaCli/MegaCli64 -LDSetProp -Cached -LALL -aALL
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ( ! grep -q 'Database optimisation' /etc/rc.local ); then
|
|
||||||
cat >> /etc/rc.local <<- EOF
|
|
||||||
# Database optimisation
|
|
||||||
echo 'never' > /sys/kernel/mm/transparent_hugepage/enabled
|
|
||||||
echo 'never' > /sys/kernel/mm/transparent_hugepage/defrag
|
|
||||||
#blockdev --setra 16384 $(echo $(blkid | awk -F':' '$1!~"block"{print $1}'))
|
|
||||||
EOF
|
|
||||||
chmod +x /etc/rc.d/rc.local
|
|
||||||
fi
|
|
||||||
|
|
||||||
cat > /etc/security/limits.d/postgresql.conf <<- EOF
|
|
||||||
postgres soft nproc 655360
|
|
||||||
postgres hard nproc 655360
|
|
||||||
postgres hard nofile 655360
|
|
||||||
postgres soft nofile 655360
|
|
||||||
postgres soft stack unlimited
|
|
||||||
postgres hard stack unlimited
|
|
||||||
postgres soft core unlimited
|
|
||||||
postgres hard core unlimited
|
|
||||||
postgres soft memlock 250000000
|
|
||||||
postgres hard memlock 250000000
|
|
||||||
EOF
|
|
||||||
cat > /etc/security/limits.d/pgbouncer.conf <<- EOF
|
|
||||||
pgbouncer soft nproc 655360
|
|
||||||
pgbouncer hard nofile 655360
|
|
||||||
pgbouncer soft nofile 655360
|
|
||||||
pgbouncer soft stack unlimited
|
|
||||||
pgbouncer hard stack unlimited
|
|
||||||
pgbouncer soft core unlimited
|
|
||||||
pgbouncer hard core unlimited
|
|
||||||
pgbouncer soft memlock 250000000
|
|
||||||
pgbouncer hard memlock 250000000
|
|
||||||
EOF
|
|
||||||
cat > /etc/security/limits.d/pgpool.conf <<- EOF
|
|
||||||
pgpool soft nproc 655360
|
|
||||||
pgpool hard nofile 655360
|
|
||||||
pgpool soft nofile 655360
|
|
||||||
pgpool soft stack unlimited
|
|
||||||
pgpool hard stack unlimited
|
|
||||||
pgpool soft core unlimited
|
|
||||||
pgpool hard core unlimited
|
|
||||||
pgpool soft memlock 250000000
|
|
||||||
pgpool hard memlock 250000000
|
|
||||||
EOF
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# ##########################################################
|
|
||||||
# postgresql config file
|
|
||||||
# args:
|
|
||||||
# arg 1: postgresql superuser
|
|
||||||
# arg 2: postgresql base directory
|
|
||||||
# arg 3: postgresql short version
|
|
||||||
# ##########################################################
|
|
||||||
pg_conf_init() {
|
|
||||||
if (( "$#" == 3 )); then
|
|
||||||
local dbsu="$1"; shift
|
|
||||||
local datadir="$1"; shift
|
|
||||||
local short_version="$1"; shift
|
|
||||||
|
|
||||||
cat > "$datadir"/conf/pg_hba.conf <<- EOF
|
|
||||||
host all $dbsu 0.0.0.0/0 reject
|
|
||||||
host monitor monitordb 0.0.0.0/0 reject
|
|
||||||
local all all md5
|
|
||||||
host replication all 0.0.0.0/0 md5
|
|
||||||
host all all 0.0.0.0/0 md5
|
|
||||||
EOF
|
|
||||||
|
|
||||||
cat > "$datadir"/conf/recovery.conf <<- EOF
|
|
||||||
standby_mode = 'on'
|
|
||||||
primary_conninfo = 'host=localhost port=5432 user=postgres password=password application_name=$(hostname)'
|
|
||||||
###restore_command = '/bin/cp -n $datadir/arcxlog/%f %p'
|
|
||||||
###restore_command = 'arcxlog=$datadir/arcxlog; /usr/bin/test -f \$arcxlog/\$(date +%Y%m%d)/%f.zip && unzip -o \$arcxlog/\$(date +%Y%m%d)/%f.zip'
|
|
||||||
###restore_command = 'arcxlog=$datadir/arcxlog; /usr/bin/test -f \$arcxlog/\$(date +%Y%m%d)/%f.lz4 && lz4 -q -d \$arcxlog/\$(date +%Y%m%d)/%f.lz4 %p'
|
|
||||||
recovery_target_timeline = 'latest'
|
|
||||||
EOF
|
|
||||||
|
|
||||||
chown -R "$dbsu":"$dbsu" "$datadir"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# ##########################################################
|
|
||||||
# postgresql initdb
|
|
||||||
# args:
|
|
||||||
# arg 1: postgresql base directory
|
|
||||||
# arg 2: postgresql superuser
|
|
||||||
# ##########################################################
|
|
||||||
pg_initdb() {
|
|
||||||
if (( "$#" == 2 )); then
|
|
||||||
local datadir="$1"; shift
|
|
||||||
local dbsu="$1"; shift
|
|
||||||
|
|
||||||
chown -R "$dbsu":"$dbsu" "$datadir"
|
|
||||||
chmod 0700 "$datadir"/data
|
|
||||||
if [[ "$( ls $datadir/data | wc -l )" == "0" ]]; then
|
|
||||||
su - "$dbsu" sh -c "source /etc/profile; initdb -D $datadir/data"
|
|
||||||
su - "$dbsu" sh -c "/bin/cp -a $datadir/data/postgresql.conf $datadir/data/postgresql.conf.bak"
|
|
||||||
su - "$dbsu" sh -c "/bin/cp -a $datadir/conf/postgresql.conf $datadir/data/postgresql.conf"
|
|
||||||
su - "$dbsu" sh -c "/bin/cp -a $datadir/data/pg_hba.conf $datadir/data/pg_hba.conf.bak"
|
|
||||||
su - "$dbsu" sh -c "/bin/cp -a $datadir/conf/pg_hba.conf $datadir/data/pg_hba.conf"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
main() {
|
|
||||||
local product_name='test'
|
|
||||||
local dbtype='postgresql'
|
|
||||||
local db_version='9.6.0'
|
|
||||||
local major_version='9.6'
|
|
||||||
local short_version='96'
|
|
||||||
local superuser='postgres'
|
|
||||||
local dbbase=""
|
|
||||||
local role="slave"
|
|
||||||
|
|
||||||
check_exec_user
|
|
||||||
|
|
||||||
while (( "$#" >= 0 )); do
|
|
||||||
case "$1" in
|
|
||||||
-U|--superuser=*)
|
|
||||||
if [[ "$1" == "-U" ]]; then
|
|
||||||
shift
|
|
||||||
fi
|
|
||||||
superuser="${1##*=}"
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
-P|--product=*)
|
|
||||||
if [[ "$1" == "-P" ]]; then
|
|
||||||
shift
|
|
||||||
fi
|
|
||||||
product_name="${1##*=}"
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
-B|--dbbase=*)
|
|
||||||
if [[ "$1" == "-B" ]]; then
|
|
||||||
shift
|
|
||||||
fi
|
|
||||||
dbbase="${1##*=}"
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
-R|--role=*)
|
|
||||||
if [[ "$1" == "-R" ]]; then
|
|
||||||
shift
|
|
||||||
fi
|
|
||||||
role="${1##*=}"
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
-V|--dbversion=*)
|
|
||||||
if [[ "$1" == "-V" ]]; then
|
|
||||||
shift
|
|
||||||
fi
|
|
||||||
db_version="${1##*=}"
|
|
||||||
major_version="${db_version:0:3}"
|
|
||||||
short_version="$(echo $db_version | awk -F'.' '{print $1$2}')"
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
-h|--help)
|
|
||||||
usage
|
|
||||||
exit
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
break
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
dbtype="postgresql"
|
|
||||||
|
|
||||||
if [[ -z "$dbbase" ]]; then
|
|
||||||
if [[ "$product_name" != "" ]] && [[ "$short_version" != "" ]]; then
|
|
||||||
dbbase="$DIR_BASE/$dbtype/${product_name}_${short_version}"
|
|
||||||
else
|
|
||||||
dbbase="$DIR_BASE/$dbtype"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
user_init "$superuser"
|
|
||||||
dir_init "$superuser" "$dbbase"
|
|
||||||
|
|
||||||
pg_install "$db_version"
|
|
||||||
pg_conf_init "$superuser" "$dbbase" "$short_version"
|
|
||||||
|
|
||||||
if [[ "$role" == "master" ]]; then
|
|
||||||
shared_xlog "$dbbase"
|
|
||||||
pg_initdb "$dbbase" "$superuser"
|
|
||||||
fi
|
|
||||||
|
|
||||||
optimize "$dbbase"
|
|
||||||
}
|
|
||||||
|
|
||||||
main "$@"
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 从源码编译安装
|
|
||||||
|
|
||||||
```bash
|
|
||||||
./configure
|
|
||||||
make
|
|
||||||
su
|
|
||||||
make install
|
|
||||||
adduser postgres
|
|
||||||
mkdir /usr/local/pgsql/data
|
|
||||||
chown postgres /usr/local/pgsql/data
|
|
||||||
su - postgres
|
|
||||||
/usr/local/pgsql/bin/initdb -D /usr/local/pgsql/data
|
|
||||||
/usr/local/pgsql/bin/postgres -D /usr/local/pgsql/data >logfile 2>&1 &
|
|
||||||
/usr/local/pgsql/bin/createdb test
|
|
||||||
/usr/local/pgsql/bin/psql test
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 编译参数
|
|
||||||
|
|
||||||
# [16.4. 安装过程]()
|
|
||||||
|
|
||||||
1.
|
|
||||||
|
|
||||||
**配置**
|
|
||||||
|
|
||||||
安装过程的第一步就是为你的系统配置源代码树并选择你喜欢的选项。这个工作是通过运行`configure`脚本实现的,对于默认安装,你只需要简单地输入:
|
|
||||||
|
|
||||||
```
|
|
||||||
./configure
|
|
||||||
```
|
|
||||||
|
|
||||||
该脚本将运行一些测试来决定一些系统相关的变量, 并检测你的操作系统的特殊设置,并且最后将在编译树中创建一些文件以记录它找到了什么。如果你想保持编译目录的独立,你也可以在一个源码树之外的目录中运行`configure` 。这个过程也被称为一个*VPATH*编译。做法如下:
|
|
||||||
|
|
||||||
```
|
|
||||||
mkdir build_dir
|
|
||||||
cd build_dir
|
|
||||||
/path/to/source/tree/configure [options go here]
|
|
||||||
make
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
默认设置将编译服务器和辅助程序,还有只需要 C 编译器的所有客户端程序和接口。默认时所有文件都将安装到`/usr/local/pgsql`。
|
|
||||||
|
|
||||||
你可以通过给出下面的`configure`命令行选项中的一个或更多的选项来自定义编译和安装过程:
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
- `--prefix=*PREFIX*`
|
|
||||||
|
|
||||||
把所有文件装在目录`*PREFIX*`中而不是`/usr/local/pgsql`中。 实际的文件会安装到数个子目录中;没有一个文件会直接安装到`*PREFIX*`目录里。如果你有特殊需要,你还可以用下面的选项自定义不同的子目录的位置。 不过,如果你把这些设置保留默认,那么安装将是可重定位的,意思是你可以在安装过后移动目录(`man`和`doc`位置不受此影响)。对于可重定位的安装,你可能需要使用`configure`的`--disable-rpath`选项。 还有,你需要告诉操作系统如何找到共享库。
|
|
||||||
|
|
||||||
- `--exec-prefix=*EXEC-PREFIX*`
|
|
||||||
|
|
||||||
你可以把体系相关的文件安装到一个不同的前缀下(`*EXEC-PREFIX*`),而不是`*PREFIX*`中设置的地方。 这样做可以比较方便地在不同主机之间共享体系相关的文件。 如果你省略这些,那么`*EXEC-PREFIX*`就会被设置为等于 `*PREFIX*`并且体系相关和体系无关的文件都会安装到同一棵目录树下,这也可能是你想要的。
|
|
||||||
|
|
||||||
- `--bindir=*DIRECTORY*`
|
|
||||||
|
|
||||||
为可执行程序指定目录。默认是`*EXEC-PREFIX*/bin`, 通常也就是`/usr/local/pgsql/bin`。
|
|
||||||
|
|
||||||
- `--sysconfdir=*DIRECTORY*`
|
|
||||||
|
|
||||||
用于各种各样配置文件的目录,默认为`*PREFIX*/etc`。
|
|
||||||
|
|
||||||
- `--libdir=*DIRECTORY*`
|
|
||||||
|
|
||||||
设置安装库和动态装载模块的目录。默认是`*EXEC-PREFIX*/lib`。
|
|
||||||
|
|
||||||
- `--includedir=*DIRECTORY*`
|
|
||||||
|
|
||||||
C 和 C++ 头文件的目录。默认是`*PREFIX*/include`。
|
|
||||||
|
|
||||||
- `--datarootdir=*DIRECTORY*`
|
|
||||||
|
|
||||||
设置多种只读数据文件的根目录。这只为后面的某些选项设置默认值。默认值为`*PREFIX*/share`。
|
|
||||||
|
|
||||||
- `--datadir=*DIRECTORY*`
|
|
||||||
|
|
||||||
设置被安装的程序使用的只读数据文件的目录。默认值为`*DATAROOTDIR*`。注意这不会对你的数据库文件被放置的位置产生任何影响。
|
|
||||||
|
|
||||||
- `--localedir=*DIRECTORY*`
|
|
||||||
|
|
||||||
设置安装区域数据的目录,特别是消息翻译目录文件。默认值为`*DATAROOTDIR*/locale`。
|
|
||||||
|
|
||||||
- `--mandir=*DIRECTORY*`
|
|
||||||
|
|
||||||
PostgreSQL自带的手册页将安装到这个目录,它们被安装在相应的`man*x*`子目录里。 默认是`*DATAROOTDIR*/man`。
|
|
||||||
|
|
||||||
- `--docdir=*DIRECTORY*`
|
|
||||||
|
|
||||||
设置安装文档文件的根目录,"man"页不包含在内。这只为后续选项设置默认值。这个选项的默认值为`*DATAROOTDIR*/doc/postgresql`。
|
|
||||||
|
|
||||||
- `--htmldir=*DIRECTORY*`
|
|
||||||
|
|
||||||
PostgreSQL的HTML格式的文档将被安装在这个目录中。默认值为`*DATAROOTDIR*`。
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
> **注意: **为了让PostgreSQL能够安装在一些共享的安装位置(例如`/usr/local/include`), 同时又不至于和系统其它部分产生名字空间干扰,我们特别做了一些处理。 首先,安装脚本会自动给`datadir`、`sysconfdir`和`docdir`后面附加上"`/postgresql`"字符串, 除非展开的完整路径名已经包含字符串"`postgres`"或者"`pgsql`"。 例如,如果你选择`/usr/local`作为前缀, 那么文档将安装在`/usr/local/doc/postgresql`,但如果前缀是`/opt/postgres`, 那么它将被放到`/opt/postgres/doc`。客户接口的公共 C 头文件安装到了`includedir`,并且是名字空间无关的。内部的头文件和服务器头文件都安装在`includedir`下的私有目录中。参考每种接口的文档获取关于如何访问头文件的信息。最后,如果合适,那么也会在`libdir`下创建一个私有的子目录用于动态可装载的模块。
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
- `--with-extra-version=*STRING*`
|
|
||||||
|
|
||||||
把`*STRING*`追加到 PostgreSQL 版本号。例如,你可以使用它来标记从未发布的 Git 快照或者包含定制补丁(带有一个如`git describe`标识符之类的额外版本号或者一个分发包发行号)创建的二进制文件。
|
|
||||||
|
|
||||||
- `--with-includes=*DIRECTORIES*`
|
|
||||||
|
|
||||||
`*DIRECTORIES*`是一个冒号分隔的目录列表,这些目录将被加入编译器的头文件搜索列表中。 如果你有一些可选的包(例如 GNU Readline)安装在非标准位置, 你就必须使用这个选项,以及可能还有相应的 `--with-libraries`选项。例子:`--with-includes=/opt/gnu/include:/usr/sup/include`.
|
|
||||||
|
|
||||||
- `--with-libraries=*DIRECTORIES*`
|
|
||||||
|
|
||||||
`*DIRECTORIES*`是一个冒号分隔的目录列表,这些目录是用于查找库文件的。 如果你有一些包安装在非标准位置,你可能就需要使用这个选项(以及对应的`--with-includes`选项)。例子:`--with-libraries=/opt/gnu/lib:/usr/sup/lib`.
|
|
||||||
|
|
||||||
- `--enable-nls[=*LANGUAGES*]`
|
|
||||||
|
|
||||||
打开本地语言支持(NLS),也就是以非英文显示程序消息的能力。`*LANGUAGES*`是一个空格分隔的语言代码列表, 表示你想支持的语言。例如`--enable-nls='de fr'` (你提供的列表和实际支持的列表之间的交集将会自动计算出来)。如果你没有声明一个列表,那么就会安装所有可用的翻译。要使用这个选项,你需要一个Gettext API 的实现。见上文。
|
|
||||||
|
|
||||||
- `--with-pgport=*NUMBER*`
|
|
||||||
|
|
||||||
把`*NUMBER*`设置为服务器和客户端的默认端口。默认是 5432。 这个端口可以在以后修改,不过如果你在这里声明,那么服务器和客户端将有相同的编译好了的默认值。这样会非常方便些。 通常选取一个非默认值的理由是你企图在同一台机器上运行多个PostgreSQL服务器。
|
|
||||||
|
|
||||||
- `--with-perl`
|
|
||||||
|
|
||||||
制作PL/Perl服务器端编程语言。
|
|
||||||
|
|
||||||
- `--with-python`
|
|
||||||
|
|
||||||
制作PL/Python服务器端编程语言。
|
|
||||||
|
|
||||||
- `--with-tcl`
|
|
||||||
|
|
||||||
制作PL/Tcl服务器编程语言。
|
|
||||||
|
|
||||||
- `--with-tclconfig=*DIRECTORY*`
|
|
||||||
|
|
||||||
Tcl 安装文件`tclConfig.sh`,其中里面包含编译与 Tcl 接口的模块的配置信息。该文件通常可以自动地在一个众所周知的位置找到,但是如果你需要一个不同版本的 Tcl,你也可以指定可以找到它的目录。
|
|
||||||
|
|
||||||
- `--with-gssapi`
|
|
||||||
|
|
||||||
编译 GSSAPI 认证支持。在很多系统上,GSSAPI(通常是 Kerberos 安装的一部分)系统不会被安装在默认搜索位置(例如`/usr/include`、`/usr/lib`),因此你必须使用选项`--with-includes`和`--with-libraries`来配合该选项。`configure`将会检查所需的头文件和库以确保你的 GSSAPI 安装足以让配置继续下去。
|
|
||||||
|
|
||||||
- `--with-krb-srvnam=*NAME*`
|
|
||||||
|
|
||||||
默认的 Kerberos 服务主的名称(也被 GSSAPI 使用)。默认是`postgres`。通常没有理由改变这个值,除非你是一个 Windows 环境,这种情况下该名称必须被设置为大写形式`POSTGRES`。
|
|
||||||
|
|
||||||
- `--with-openssl`
|
|
||||||
|
|
||||||
编译SSL(加密)连接支持。这个选项需要安装OpenSSL包。`configure`将会检查所需的头文件和库以确保你的 OpenSSL安装足以让配置继续下去。
|
|
||||||
|
|
||||||
- `--with-pam`
|
|
||||||
|
|
||||||
编译PAM(可插拔认证模块)支持。
|
|
||||||
|
|
||||||
- `--with-bsd-auth`
|
|
||||||
|
|
||||||
编译 BSD 认证支持(BSD 认证框架目前只在 OpenBSD 上可用)。
|
|
||||||
|
|
||||||
- `--with-ldap`
|
|
||||||
|
|
||||||
为认证和连接参数查找编译LDAP支持(详见[第 32.17 节](http://www.postgres.cn/docs/9.6/libpq-ldap.html)和[第 20.3.7 节](http://www.postgres.cn/docs/9.6/auth-methods.html#AUTH-LDAP))。在 Unix 上,这需要安装OpenLDAP包。在 Windows 上将使用默认的WinLDAP库。`configure`将会检查所需的头文件和库以确保你的 OpenLDAP安装足以让配置继续下去。
|
|
||||||
|
|
||||||
- `--with-systemd`
|
|
||||||
|
|
||||||
编译对systemd 服务通知的支持。如果服务器是在systemd 机制下被启动,这可以提高集成度,否则不会有影响 ; see [第 18.3 节](http://www.postgres.cn/docs/9.6/server-start.html) for more information。要使用这个选项,必须安装libsystemd 以及相关的头文件。
|
|
||||||
|
|
||||||
- `--without-readline`
|
|
||||||
|
|
||||||
避免使用Readline库(以及libedit)。这个选项禁用了psql中的命令行编辑和历史, 因此我们不建议这么做。
|
|
||||||
|
|
||||||
- `--with-libedit-preferred`
|
|
||||||
|
|
||||||
更倾向于使用BSD许可证的libedit库而不是GPL许可证的Readline。这个选项只有在你同时安装了两个库时才有意义,在那种情况下默认会使用Readline。
|
|
||||||
|
|
||||||
- `--with-bonjour`
|
|
||||||
|
|
||||||
编译 Bonjour 支持。这要求你的操作系统支持 Bonjour。在 OS X 上建议使用。
|
|
||||||
|
|
||||||
- `--with-uuid=*LIBRARY*`
|
|
||||||
|
|
||||||
使用指定的 UUID 库编译 [uuid-ossp](http://www.postgres.cn/docs/9.6/uuid-ossp.html)模块(提供生成 UUID 的函数)。 `*LIBRARY*`必须是下列之一:`bsd`,用来使用 FreeBSD、NetBSD 和一些其他 BSD 衍生系统 中的 UUID 函数`e2fs`,用来使用`e2fsprogs`项目创建的 UUID 库, 这个库出现在大部分的 Linux 系统和 OS X 中,并且也能找到用于其他平台的 版本`ossp`,用来使用[OSSP UUID library](http://www.ossp.org/pkg/lib/uuid/)
|
|
||||||
|
|
||||||
- `--with-ossp-uuid`
|
|
||||||
|
|
||||||
`--with-uuid=ossp`的已废弃的等效选项。
|
|
||||||
|
|
||||||
- `--with-libxml`
|
|
||||||
|
|
||||||
编译 libxml (启用 SQL/XML 支持)。这个特性需要 Libxml 版本 2.6.23 及以上。Libxml 会安装一个程序`xml2-config`,它可以被用来检测所需的编译器和链接器选项。如果能找到,PostgreSQL 将自动使用它。要制定一个非常用的 libxml 安装位置,你可以设置环境变量`XML2_CONFIG`指向`xml2-config`程序所属的安装,或者使用选项`--with-includes`和`--with-libraries`。
|
|
||||||
|
|
||||||
- `--with-libxslt`
|
|
||||||
|
|
||||||
编译 [xml2](http://www.postgres.cn/docs/9.6/xml2.html)模块时使用 libxslt。xml2依赖这个库来执行XML的XSL转换。
|
|
||||||
|
|
||||||
- `--disable-integer-datetimes`
|
|
||||||
|
|
||||||
禁用对时间戳和间隔的64位存储支持,并且将 datetime 值存储为浮点数。浮点 datetime 存储在PostgreSQL 8.4之前是默认方式,但是现在已经被废弃,因为它对于`timestamp`值的全范围不支持毫秒精度。但是,基于整数的 datetime 存储要求64位整数类型。因此,当没有64位整数类型时,可以使用这个选项,或者在兼容为PostgreSQL之前版本开发的应用时使用。详见 [第 8.5 节](http://www.postgres.cn/docs/9.6/datatype-datetime.html)。
|
|
||||||
|
|
||||||
- `--disable-float4-byval`
|
|
||||||
|
|
||||||
禁用 float4 值的"传值",导致它们只能被"传引用"。这个选项会损失性能,但是在需要兼容使用 C 编写并使用"version 0"调用规范的老用户定义函数时可能需要这个选项。更好的长久解决方案是将任何这样的函数更新成使用"version 1"调用规范。
|
|
||||||
|
|
||||||
- `--disable-float8-byval`
|
|
||||||
|
|
||||||
禁用 float8 值的"传值",导致它们只能被"传引用"。这个选项会损失性能,但是在需要兼容使用 C 编写并使用"version 0"调用规范的老用户定义函数时可能需要这个选项。更好的长久解决方案是将任何这样的函数更新成使用"version 1"调用规范。注意这个选项并非只影响 float8,它还影响 int8 和某些相关类型如时间戳。在32位平台上,`--disable-float8-byval`是默认选项并且不允许选择`--enable-float8-byval`。
|
|
||||||
|
|
||||||
- `--with-segsize=*SEGSIZE*`
|
|
||||||
|
|
||||||
设置*段尺寸*,以 G 字节计。大型的表会被分解成多个操作系统文件,每一个的尺寸等于段尺寸。这避免了与操作系统对文件大小限制相关的问题。默认的段尺寸(1G字节)在所有支持的平台上都是安全的。如果你的操作系统有"largefile"支持(如今大部分都支持),你可以使用一个更大的段尺寸。这可以有助于在使用非常大的表时消耗的文件描述符数目。但是要当心不能选择一个超过你将使用的平台和文件系统所支持尺寸的值。你可能希望使用的其他工具(如tar)也可以对可用文件尺寸设限。如非绝对必要,我们推荐这个值应为2的幂。注意改变这个值需要一次 initdb。
|
|
||||||
|
|
||||||
- `--with-blocksize=*BLOCKSIZE*`
|
|
||||||
|
|
||||||
设置*块尺寸*,以 K 字节计。这是表内存储和I/O的单位。默认值(8K字节)适合于大多数情况,但是在特殊情况下可能其他值更有用。这个值必须是2的幂并且在 1 和 32 (K字节)之间。注意修改这个值需要一次 initdb。
|
|
||||||
|
|
||||||
- `--with-wal-segsize=*SEGSIZE*`
|
|
||||||
|
|
||||||
设置*WAL 段尺寸*,以 M 字节计。这是 WAL 日志中每一个独立文件的尺寸。调整这个值来控制传送 WAL 日志的粒度非常有用。默认尺寸为 16 M字节。这个值必须是2的幂并且在 1 到 64 (M字节)之间。注意修改这个值需要一次 initdb。
|
|
||||||
|
|
||||||
- `--with-wal-blocksize=*BLOCKSIZE*`
|
|
||||||
|
|
||||||
设置*WAL 块尺寸*,以 K 字节计。这是 WAL 日志存储和I/O的单位。默认值(8K 字节)适合于大多数情况,但是在特殊情况下其他值更好有用。这个值必须是2的幂并且在 1 到 64(K字节)之间。注意修改这个值需要一次 initdb。
|
|
||||||
|
|
||||||
- `--disable-spinlocks`
|
|
||||||
|
|
||||||
即便PostgreSQL对于该平台没有 CPU 自旋锁支持,也允许编译成功。自旋锁支持的缺乏会导致较差的性能,因此这个选项只有当编译终端或者通知你该平台缺乏自旋锁支持时才应被使用。如果在你的平台上要求使用该选项来编译PostgreSQL,请将此问题报告给PostgreSQL的开发者。
|
|
||||||
|
|
||||||
- `--disable-thread-safety`
|
|
||||||
|
|
||||||
禁用客户端库的线程安全性。这会阻止libpq和ECPG程序中的并发线程安全地控制它们私有的连接句柄。
|
|
||||||
|
|
||||||
- `--with-system-tzdata=*DIRECTORY*`
|
|
||||||
|
|
||||||
PostgreSQL包含它自己的时区数据库,它被用于日期和时间操作。这个时区数据库实际上是和 IANA 时区数据库相兼容的,后者在很多操作系统如 FreeBSD、Linux和Solaris上都有提供,因此再次安装它可能是冗余的。当这个选项被使用时,将不会使用`*DIRECTORY*`中系统提供的时区数据库,而是使用包括在 PostgreSQL 源码发布中的时区数据库。`*DIRECTORY*`必须被指定为一个绝对路径。`/usr/share/zoneinfo`在某些操作系统上是一个很有可能的路径。注意安装例程将不会检测不匹配或错误的时区数据。如果你使用这个选项,建议你运行回归测试来验证你指定的时区数据能正常地工作在PostgreSQL中。这个选项主要针对那些很了解他们的目标操作系统的二进制包发布者。使用这个选项主要优点是不管何时当众多本地夏令时规则之一改变时, PostgreSQL 包不需要被升级。另一个优点是如果时区数据库文件在安装时不需要被编译, PostgreSQL 可以被更直接地交叉编译。
|
|
||||||
|
|
||||||
- `--without-zlib`
|
|
||||||
|
|
||||||
避免使用Zlib库。这样就禁用了pg_dump和 pg_restore中对压缩归档的支持。这个选项只适用于那些没有这个库的少见的系统。
|
|
||||||
|
|
||||||
- `--enable-debug`
|
|
||||||
|
|
||||||
把所有程序和库以带有调试符号的方式编译。这意味着你可以通过一个调试器运行程序来分析问题。 这样做显著增大了最后安装的可执行文件的大小,并且在非 GCC 的编译器上,这么做通常还要关闭编译器优化, 这些都导致速度的下降。但是,如果有这些符号的话,就可以非常有效地帮助定位可能发生问题的位置。目前,我们只是在你使用 GCC 的情况下才建议在生产安装中使用这个选项。但是如果你正在进行开发工作,或者正在使用 beta 版本,那么你就应该总是打开它。
|
|
||||||
|
|
||||||
- `--enable-coverage`
|
|
||||||
|
|
||||||
如果在使用 GCC,所有程序和库都会用代码覆盖率测试工具编译。在运行时,它们会在编译目录中生成代码覆盖率度量的文件。详见[第 31.5 节](http://www.postgres.cn/docs/9.6/regress-coverage.html)。这个选项只用于 GCC 以及做开发工作时。
|
|
||||||
|
|
||||||
- `--enable-profiling`
|
|
||||||
|
|
||||||
如果在使用 GCC,所有程序和库都被编译成可以进行性能分析。在后端退出时,将会创建一个子目录,其中包含用于性能分析的`gmon.out`文件。这个选项只用于 GCC 和做开发工作时。
|
|
||||||
|
|
||||||
- `--enable-cassert`
|
|
||||||
|
|
||||||
打开在服务器中的*assertion*检查, 它会检查许多"不可能发生"的条件。它对于代码开发的用途而言是无价之宝, 不过这些测试可能会显著地降低服务器的速度。并且,打开这个测试不会提高你的系统的稳定性! 这些断言检查并不是按照严重性分类的,因此一些相对无害的小故障也可能导致服务器重启 — 只要它触发了一次断言失败。 目前,我们不推荐在生产环境中使用这个选项,但是如果你在做开发或者在使用 beta 版本的时候应该打开它。
|
|
||||||
|
|
||||||
- `--enable-depend`
|
|
||||||
|
|
||||||
打开自动倚赖性跟踪。如果打开这个选项,那么制作文件(makefile)将设置为在任何头文件被修改的时候都将重新编译所有受影响的目标文件。 如果你在做开发的工作,那么这个选项很有用,但是如果你只是想编译一次并且安装,那么这就是浪费时间。 目前,这个选项只对 GCC 有用。
|
|
||||||
|
|
||||||
- `--enable-dtrace`
|
|
||||||
|
|
||||||
为PostgreSQL编译对动态跟踪工具 DTrace 的支持。 详见[第 28.5 节](http://www.postgres.cn/docs/9.6/dynamic-trace.html)。要指向`dtrace`程序,必须设置环境变量`DTRACE`。这通常是必需的,因为`dtrace`通常被安装在`/usr/sbin`中,该路径可能不在搜索路径中。`dtrace`程序的附加命令行选项可以在环境变量`DTRACEFLAGS`中指定。在 Solaris 上,要在一个64位二进制中包括 DTrace,你必须为 configure 指定`DTRACEFLAGS="-64"`。例如,使用 GCC 编译器:`./configure CC='gcc -m64' --enable-dtrace DTRACEFLAGS='-64' ...`使用 Sun 的编译器:`./configure CC='/opt/SUNWspro/bin/cc -xtarget=native64' --enable-dtrace DTRACEFLAGS='-64' ...`
|
|
||||||
|
|
||||||
- `--enable-tap-tests`
|
|
||||||
|
|
||||||
启用 Perl TAP 工具进行测试。这要求安装了 Perl 以及 Perl 模块`IPC::Run`。 详见[第 31.4 节](http://www.postgres.cn/docs/9.6/regress-tap.html)。
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
如果你喜欢用那些和`configure`选取的不同的 C 编译器,那么你可以你的环境变量`CC`设置为你选择的程序。默认时,只要`gcc`可以使用,`configure`将选择它, 或者是该平台的默认(通常是`cc`)。类似地,你可以用`CFLAGS`变量覆盖默认编译器标志。
|
|
||||||
|
|
||||||
你可以在`configure`命令行上指定环境变量, 例如:
|
|
||||||
|
|
||||||
```
|
|
||||||
./configure CC=/opt/bin/gcc CFLAGS='-O2 -pipe'
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
下面是可以以这种方式设置的有效变量的列表:
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
- `BISON`
|
|
||||||
|
|
||||||
Bison程序
|
|
||||||
|
|
||||||
- `CC`
|
|
||||||
|
|
||||||
C编译器
|
|
||||||
|
|
||||||
- `CFLAGS`
|
|
||||||
|
|
||||||
传递给 C 编译器的选项
|
|
||||||
|
|
||||||
- `CPP`
|
|
||||||
|
|
||||||
C 预处理器
|
|
||||||
|
|
||||||
- `CPPFLAGS`
|
|
||||||
|
|
||||||
传递给 C 预处理器的选项
|
|
||||||
|
|
||||||
- `DTRACE`
|
|
||||||
|
|
||||||
`dtrace`程序的位置
|
|
||||||
|
|
||||||
- `DTRACEFLAGS`
|
|
||||||
|
|
||||||
传递给`dtrace`程序的选项
|
|
||||||
|
|
||||||
- `FLEX`
|
|
||||||
|
|
||||||
Flex程序
|
|
||||||
|
|
||||||
- `LDFLAGS`
|
|
||||||
|
|
||||||
链接可执行程序或共享库时使用的选项
|
|
||||||
|
|
||||||
- `LDFLAGS_EX`
|
|
||||||
|
|
||||||
只用于链接可执行程序的附加选项
|
|
||||||
|
|
||||||
- `LDFLAGS_SL`
|
|
||||||
|
|
||||||
只用于链接共享库的附加选项
|
|
||||||
|
|
||||||
- `MSGFMT`
|
|
||||||
|
|
||||||
用于本地语言支持的`msgfmt`程序
|
|
||||||
|
|
||||||
- `PERL`
|
|
||||||
|
|
||||||
Perl 解释器的全路径。这将被用来决定编译 PL/Perl 时的依赖性。
|
|
||||||
|
|
||||||
- `PYTHON`
|
|
||||||
|
|
||||||
Python 解释器的全路径。这将被用来决定编译 PL/Python 时的依赖性。另外这里指定的是 Python 2 还是 Python 3 (或者是隐式选择)决定了 PL/Python 语言的哪一种变种将成为可用的。详见 [第 44.1 节](http://www.postgres.cn/docs/9.6/plpython-python23.html)。
|
|
||||||
|
|
||||||
- `TCLSH`
|
|
||||||
|
|
||||||
Tcl 解释器的全路径。这将被用来决定编译 PL/Tcl 时的依赖性,并且它将被替换到 Tcl 脚本中。
|
|
||||||
|
|
||||||
- `XML2_CONFIG`
|
|
||||||
|
|
||||||
用于定位 libxml 安装的`xml2-config`程序。
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
> **注意: **在开发服务器内部代码时,我们推荐使用配置选项`--enable-cassert`(它会打开很多运行时错误检查)和`--enable-debug`(它会提高调试工具的有用性)。
|
|
||||||
>
|
|
||||||
> 如果在使用 GCC,最好使用至少`-O1`的优化级别来编译,因为不使用优化(`-O0`)会禁用某些重要的编译器警告(例如使用未经初始化的变量)。但是,非零的优化级别会使调试更复杂,因为在编译好的代码中步进通常将不能和源代码行一一对应。如果你在尝试调试优化过的代码时觉得困惑,将感兴趣的特定文件使用`-O0`编译。一种简单的方式是传递一个选项给make:`make PROFILE=-O0 file.o`。
|
|
||||||
|
|
||||||
2.
|
|
||||||
|
|
||||||
**编译**
|
|
||||||
|
|
||||||
要开始编译,键入:
|
|
||||||
|
|
||||||
```
|
|
||||||
make
|
|
||||||
```
|
|
||||||
|
|
||||||
(一定要记得用GNU make)。依你的硬件而异,编译过程可能需要 5 分钟到半小时。显示的最后一行应该是:
|
|
||||||
|
|
||||||
```
|
|
||||||
All of PostgreSQL successfully made. Ready to install.
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
如果你希望编译所有能编译的东西,包括文档(HTML和手册页)以及附加模块(`contrib`),这样键入:
|
|
||||||
|
|
||||||
```
|
|
||||||
make world
|
|
||||||
```
|
|
||||||
|
|
||||||
显示的最后一行应该是:
|
|
||||||
|
|
||||||
```
|
|
||||||
PostgreSQL, contrib, and documentation successfully made. Ready to install.
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
3. **回归测试**
|
|
||||||
|
|
||||||
如果你想在安装文件前测试新编译的服务器, 那么你可以在这个时候运行回归测试。 回归测试是一个用于验证PostgreSQL在你的系统上是否按照开发人员设想的那样运行的测试套件。键入:
|
|
||||||
|
|
||||||
```
|
|
||||||
make check
|
|
||||||
```
|
|
||||||
|
|
||||||
(这条命令不能以 root 运行;请在非特权用户下运行该命令)。 (This won't work as root; do it as an unprivileged user.) [第 31 章](http://www.postgres.cn/docs/9.6/regress.html)包含关于如何解释测试结果的详细信息。你可以在以后的任何时间通过执行这条命令来运行这个测试。
|
|
||||||
|
|
||||||
4.
|
|
||||||
|
|
||||||
**安装文件**
|
|
||||||
|
|
||||||
> **注意: **如果你正在升级一套现有的系统,请阅读 [第 18.6 节](http://www.postgres.cn/docs/9.6/upgrading.html) 其中有关于升级一个集簇的指导。
|
|
||||||
|
|
||||||
要安装PostgreSQL,输入:
|
|
||||||
|
|
||||||
```
|
|
||||||
make install
|
|
||||||
```
|
|
||||||
|
|
||||||
这条命令将把文件安装到在[步骤 1](http://www.postgres.cn/docs/9.6/install-procedure.html#CONFIGURE)中指定的目录。确保你有足够的权限向该区域写入。通常你需要用 root 权限做这一步。或者你也可以事先创建目标目录并且分派合适的权限。
|
|
||||||
|
|
||||||
要安装文档(HTML和手册页),输入:
|
|
||||||
|
|
||||||
```
|
|
||||||
make install-docs
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
如果你按照上面的方法编译了所有东西,输入:
|
|
||||||
|
|
||||||
```
|
|
||||||
make install-world
|
|
||||||
```
|
|
||||||
|
|
||||||
这也会安装文档。
|
|
||||||
|
|
||||||
你可以使用`make install-strip`代替`make install`, 在安装可执行文件和库文件时把它们剥离。 这样将节约一些空间。如果你编译时带着调试支持,那么抽取将有效地删除调试支持, 因此我们应该只是在不再需要调试的时候做这些事情。 `install-strip`力图做一些合理的工作来节约空间, 但是它并不了解如何从可执行文件中抽取每个不需要的字节, 因此,如果你希望节约所有可能节约的磁盘空间,那么你可能需要手工做些处理。
|
|
||||||
|
|
||||||
标准的安装只提供客户端应用开发和服务器端程序开发所需的所有头文件,例如用 C 写的定制函数或者数据类型(在PostgreSQL 8.0 之前,后者需要独立地执行一次`make install-all-headers`命令,不过现在这个步骤已经融合到标准的安装步骤中)。
|
|
||||||
|
|
||||||
**只安装客户端:. **如果你只想装客户应用和接口,那么你可以用下面的命令:
|
|
||||||
|
|
||||||
```
|
|
||||||
make -C src/bin install
|
|
||||||
make -C src/include install
|
|
||||||
make -C src/interfaces install
|
|
||||||
make -C doc install
|
|
||||||
```
|
|
||||||
|
|
||||||
`src/bin`中有一些服务器专用的二进制文件,但是它们很小。
|
|
||||||
|
|
||||||
**卸载:. **要撤销安装可以使用命令`make uninstall`。不过这样不会删除任何创建出来的目录。
|
|
||||||
|
|
||||||
**清理:. **在安装完成以后,你可以通过在源码树里面用命令`make clean`删除编译文件。 这样会保留`configure`程序生成的文件,这样以后你就可以用`make`命令重新编译所有东西。 要把源码树恢复为发布时的状态,可用`make distclean`命令。 如果你想从同一棵源码树上为多个不同平台制作,你就一定要运行这条命令并且为每个编译重新配置(另外一种方法是在每种平台上使用一套独立的编译树,这样源代码树就可以保留不被更改)。
|
|
||||||
|
|
||||||
如果你执行了一次制作,然后发现你的`configure`选项是错误的, 或者你修改了任何`configure`所探测的东西(例如,升级了软件), 那么在重新配置和编译之前运行一下`make distclean`是个好习惯。如果不这样做, 你修改的配置选项可能无法传播到所有需要变化的地方。
|
|
@ -1,198 +0,0 @@
|
|||||||
# Monitor
|
|
||||||
|
|
||||||
## 磁盘
|
|
||||||
|
|
||||||
### 磁盘写满问题
|
|
||||||
|
|
||||||
* 一个数据库管理员最重要的磁盘监控任务就是确保磁盘不会写满。
|
|
||||||
* 一个写满了的数据磁盘可能不会导致数据的崩溃,但它肯定会让系统变得不可用。
|
|
||||||
* 如果保存 WAL 文件的磁盘变满,Server会产生致命错误,可能会关闭。
|
|
||||||
* 有些文件系统在快满的时候性能会急剧恶化,不要等到磁盘完全满时再行动。
|
|
||||||
* 可以将WAL和数据目录放到不同的磁盘上。
|
|
||||||
* 可以通过使用表空间(Tablespace)把一些数据库文件移到其他文件系统上去。
|
|
||||||
|
|
||||||
### 磁盘占用
|
|
||||||
|
|
||||||
* 每个表都有一个主要的堆磁盘文件,大多数数据都存储在其中。
|
|
||||||
* 每个表和索引都存放在单独的磁盘文件里。若超过 1G 字节,则可能多于一个文件。
|
|
||||||
* 如果一个表有很宽(>2KB)的列, 则存在一个TOAST文件之关联, 用于存储太宽的值。因为Pg里一个页大小8KB,而单个元组不允许跨页,TOAST字段长度用四字节整形表示,最高的两个比特位用于指示Toast,所以单个字段值长度不能超过1G。
|
|
||||||
|
|
||||||
### 监视磁盘
|
|
||||||
|
|
||||||
有三种方式监控磁盘空间:
|
|
||||||
|
|
||||||
* 人工观察系统目录。`du`, `df -h`
|
|
||||||
|
|
||||||
* 使用`oid2name`模块
|
|
||||||
|
|
||||||
* 使用`SQL`函数与系统视图。(推荐)
|
|
||||||
|
|
||||||
例如`pg_class`中的`RelPages`指明了对象使用的Page (8k block)的数目。可用于预估表大小。
|
|
||||||
|
|
||||||
|
|
||||||
### 常用查询
|
|
||||||
|
|
||||||
```plsql
|
|
||||||
--查询表占用空间(估计)
|
|
||||||
-- 页面大小为8k,元组条数为估计值
|
|
||||||
SELECT relname, relpages, reltuples,
|
|
||||||
pg_relation_filepath(oid), reltoastrelid
|
|
||||||
FROM pg_class where relname = 'messages';
|
|
||||||
|
|
||||||
-- 查看表索引大小
|
|
||||||
SELECT c2.relname, c2.relpages
|
|
||||||
FROM pg_class c, pg_class c2, pg_index i
|
|
||||||
WHERE c.relname = 'messages' AND
|
|
||||||
c.oid = i.indrelid AND
|
|
||||||
c2.oid = i.indexrelid
|
|
||||||
ORDER BY c2.relname DESC;
|
|
||||||
|
|
||||||
-- 查看表大小与索引大小
|
|
||||||
SELECT
|
|
||||||
relname,
|
|
||||||
relpages,
|
|
||||||
reltuples,
|
|
||||||
pg_relation_filepath(oid),
|
|
||||||
pg_size_pretty(pg_relation_size(oid)) AS rel_size,
|
|
||||||
pg_size_pretty(pg_indexes_size(oid)) AS idx_size,
|
|
||||||
reltoastrelid
|
|
||||||
FROM pg_class
|
|
||||||
WHERE relname = 'poi';
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 锁
|
|
||||||
|
|
||||||
未解决的锁可以通过系统视图 `pg_locks`查看
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Vacuum进度
|
|
||||||
|
|
||||||
目前只有Vacuum命令可以查看执行进度。
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pg_stat_progress_vacuum
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 指标
|
|
||||||
|
|
||||||
### 进程监控
|
|
||||||
|
|
||||||
使用Unix工具,可以查看工作中的postgres进程
|
|
||||||
|
|
||||||
```bash
|
|
||||||
ps auxww | grep ^postgres
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ ps auxww | grep ^postgres
|
|
||||||
postgres 15551 0.0 0.1 57536 7132 pts/0 S 18:02 0:00 postgres -i
|
|
||||||
postgres 15554 0.0 0.0 57536 1184 ? Ss 18:02 0:00 postgres: writer process
|
|
||||||
postgres 15555 0.0 0.0 57536 916 ? Ss 18:02 0:00 postgres: checkpointer process
|
|
||||||
postgres 15556 0.0 0.0 57536 916 ? Ss 18:02 0:00 postgres: wal writer process
|
|
||||||
postgres 15557 0.0 0.0 58504 2244 ? Ss 18:02 0:00 postgres: autovacuum launcher process
|
|
||||||
postgres 15558 0.0 0.0 17512 1068 ? Ss 18:02 0:00 postgres: stats collector process
|
|
||||||
postgres 15582 0.0 0.0 58772 3080 ? Ss 18:04 0:00 postgres: joe runbug 127.0.0.1 idle
|
|
||||||
postgres 15606 0.0 0.0 58772 3052 ? Ss 18:07 0:00 postgres: tgl regression [local] SELECT waiting
|
|
||||||
postgres 15610 0.0 0.0 58772 3056 ? Ss 18:07 0:00 postgres: tgl regression [local] idle in transaction
|
|
||||||
```
|
|
||||||
|
|
||||||
例如,空连接会提示`[idle]`,`begin`的事务会提示`idle in transaction`,如果进程在执行某条命令,会显示在进程的名称之中。
|
|
||||||
|
|
||||||
其他实用的监控工具包括:`ps`,`top`,`iostat`,`vmstat`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 统计收集
|
|
||||||
|
|
||||||
### 相关选项
|
|
||||||
|
|
||||||
| name | type | default | comment |
|
|
||||||
| ---------------------------- | ---------- | ------------- | -------------------------------------- |
|
|
||||||
| `track_activities` | `boolean` | `true` | 启用对每个会话的当前执行命令的信息收集,还有命令开始执行的时间 |
|
|
||||||
| ` track_activity_query_size` | ` integer` | `1024` | 声明保留的字节数,以跟踪每个活动会话的当前执行命令 |
|
|
||||||
| ` track_counts` | `boolean` | `true` | 启用在数据库活动上的统计收集,Vacuum需要这个统计 |
|
|
||||||
| ` track_io_timing` | `boolean` | `false` | 启用对系统 I/O 调用的计时。 |
|
|
||||||
| ` track_functions` | `enum` | `none` | 指定`pl`只跟踪过程语言函数,指定`all`跟踪 SQL 和 C 语言函数 |
|
|
||||||
| ` stats_temp_directory` | `string` | `pg_stat_tmp` | 统计数据临时目录路径 |
|
|
||||||
|
|
||||||
* 统计进程通过临时文件将统计数据传递给其他PostgreSQL进程
|
|
||||||
* 临时的统计信息放在`pg_stat_tmp`目录下,这个目录可以通过RAMDISK获得更好的性能。
|
|
||||||
* 当服务器关闭时,统计数据的拷贝会放在`pg_stat`目录下。
|
|
||||||
* 当服务器启动恢复时,所有统计计数器会重置。
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 查看统计数据
|
|
||||||
|
|
||||||
* 统计数据并非实时更新的,每个服务进程只有在闲置前会更新统计计数。所以正在执行的查询和事务不影响计数。
|
|
||||||
* 收集器本身每隔(PGSTAT_STAT_INTERVAL=500ms)才发送一次新的报告。所以除了 当前进程活动`track_activity`之外的统计指标都不是最新的。
|
|
||||||
* 在事务中执行的统计查询,统计数据不会发生变化。使用`pg_stat_clear_snapshot()`来获取最新的快照。
|
|
||||||
|
|
||||||
### 系统视图名称
|
|
||||||
|
|
||||||
| 视图名称 | 描述 |
|
|
||||||
| ----------------------------- | ---------------------------------------- |
|
|
||||||
| `pg_stat_archiver` | 只有一行,显示有关 WAL 归档进程活动的统计信息。详见[pg_stat_archiver](http://www.postgres.cn/docs/9.6/monitoring-stats.html#PG-STAT-ARCHIVER-VIEW)。 |
|
|
||||||
| `pg_stat_bgwriter` | 只有一行,显示有关后台写进程的活动的统计信息。详见[pg_stat_bgwriter](http://www.postgres.cn/docs/9.6/monitoring-stats.html#PG-STAT-BGWRITER-VIEW)。 |
|
|
||||||
| `pg_stat_database` | 每个数据库一行,显示数据库范围的统计信息。详见[pg_stat_database](http://www.postgres.cn/docs/9.6/monitoring-stats.html#PG-STAT-DATABASE-VIEW)。 |
|
|
||||||
| `pg_stat_database_conflicts` | 每个数据库一行,显示数据库范围的统计信息, 这些信息的内容是关于由于与后备服务器的恢复过程 发生冲突而被取消的查询。详见[pg_stat_database_conflicts](http://www.postgres.cn/docs/9.6/monitoring-stats.html#PG-STAT-DATABASE-CONFLICTS-VIEW)。 |
|
|
||||||
| `pg_stat_all_tables` | 当前数据库中每个表一行,显示有关访问指定表的统计信息。详见[pg_stat_all_tables](http://www.postgres.cn/docs/9.6/monitoring-stats.html#PG-STAT-ALL-TABLES-VIEW)。 |
|
|
||||||
| `pg_stat_sys_tables` | 和`pg_stat_all_tables`一样,但只显示系统表。 |
|
|
||||||
| `pg_stat_user_tables` | 和`pg_stat_all_tables`一样,但只显示用户表。 |
|
|
||||||
| `pg_stat_xact_all_tables` | 和`pg_stat_all_tables`相似,但计数动作只在当前事务内发生(还*没有*被包括在`pg_stat_all_tables`和相关视图中)。用于生存和死亡行数量的列以及清理和分析动作在此视图中不出现。 |
|
|
||||||
| `pg_stat_xact_sys_tables` | 和`pg_stat_xact_all_tables`一样,但只显示系统表。 |
|
|
||||||
| `pg_stat_xact_user_tables` | 和`pg_stat_xact_all_tables`一样,但只显示用户表。 |
|
|
||||||
| `pg_stat_all_indexes` | 当前数据库中的每个索引一行,显示:表OID、索引OID、模式名、表名、索引名、 使用了该索引的索引扫描总数、索引扫描返回的索引记录数、使用该索引的简 单索引扫描抓取的活表(livetable)中数据行数。 当前数据库中的每个索引一行,显示与访问指定索引有关的统计信息。详见[pg_stat_all_indexes](http://www.postgres.cn/docs/9.6/monitoring-stats.html#PG-STAT-ALL-INDEXES-VIEW)。 |
|
|
||||||
| `pg_stat_sys_indexes` | 和`pg_stat_all_indexes`一样,但只显示系统表上的索引。 |
|
|
||||||
| `pg_stat_user_indexes` | 和`pg_stat_all_indexes`一样,但只显示用户表上的索引。 |
|
|
||||||
| `pg_statio_all_tables` | 当前数据库中每个表一行(包括TOAST表),显示:表OID、模式名、表名、 从该表中读取的磁盘块总数、缓冲区命中次数、该表上所有索引的磁盘块读取总数、 该表上所有索引的缓冲区命中总数、在该表的辅助TOAST表(如果存在)上的磁盘块读取总数、 在该表的辅助TOAST表(如果存在)上的缓冲区命中总数、TOAST表的索引的磁盘块读 取总数、TOAST表的索引的缓冲区命中总数。 当前数据库中的每个表一行,显示有关在指定表上 I/O 的统计信息。详见[pg_statio_all_tables](http://www.postgres.cn/docs/9.6/monitoring-stats.html#PG-STATIO-ALL-TABLES-VIEW)。 |
|
|
||||||
| `pg_statio_sys_tables` | 和`pg_statio_all_tables`一样,但只显示系统表。 |
|
|
||||||
| `pg_statio_user_tables` | 和`pg_statio_all_tables`一样,但只显示用户表。 |
|
|
||||||
| `pg_statio_all_indexes` | 当前数据库中每个索引一行,显示:表OID、索引OID、模式名、 表名、索引名、该索引的磁盘块读取总数、该索引的缓冲区命中总数。 当前数据库中的每个索引一行,显示与指定索引上的 I/O 有关的统计信息。详见[pg_statio_all_indexes](http://www.postgres.cn/docs/9.6/monitoring-stats.html#PG-STATIO-ALL-INDEXES-VIEW)。 |
|
|
||||||
| `pg_statio_sys_indexes` | 和`pg_statio_all_indexes`一样,但只显示系统表上的索引。 |
|
|
||||||
| `pg_statio_user_indexes` | 和`pg_statio_all_indexes`一样,但只显示用户表上的索引。 |
|
|
||||||
| `pg_statio_all_sequences` | 当前数据库中每个序列对象一行,显示:序列OID、模式名、序列名、序列的磁盘读取总数、序列的缓冲区命中总数。 当前数据库中的每个序列一行,显示与指定序列上的 I/O 有关的统计信息。详见[pg_statio_all_sequences](http://www.postgres.cn/docs/9.6/monitoring-stats.html#PG-STATIO-ALL-SEQUENCES-VIEW)。 |
|
|
||||||
| `pg_statio_sys_sequences` | 和`pg_statio_all_sequences`一样,但只显示系统序列(目前没有定义系统序列,因此这个视图总是为空)。 |
|
|
||||||
| `pg_statio_user_sequences` | 和`pg_statio_all_sequences`一样,但只显示用户序列。 |
|
|
||||||
| `pg_stat_user_functions` | 对于所有跟踪功能,函数的OID,模式,名称,数量 通话总时间,和自我的时间。自我时间是 在函数本身所花费的时间量,总时间包括 它调用函数所花费的时间。时间值以毫秒为单位。 每一个被跟踪的函数一行,显示与执行该函数有关的统计信息。详见[pg_stat_user_functions](http://www.postgres.cn/docs/9.6/monitoring-stats.html#PG-STAT-USER-FUNCTIONS-VIEW)。 |
|
|
||||||
| `pg_stat_xact_user_functions` | 和`pg_stat_user_functions`相似,但是只统计在当前事务期间的调用(还*没有*被包括在`pg_stat_user_functions`中)。 |
|
|
||||||
| `pg_stat_progress_vacuum` | 每个运行`VACUUM`的后端(包括自动清理工作者进程)一行,显示当前的进度。见[第 28.4.1 节](http://www.postgres.cn/docs/9.6/progress-reporting.html#VACUUM-PROGRESS-REPORTING)。 |
|
|
||||||
|
|
||||||
### 常用统计语句
|
|
||||||
|
|
||||||
```plsql
|
|
||||||
-- 按照拉取数据条数降序,查看所有表。
|
|
||||||
SELECT * FROM pg_stat_user_tables ORDER BY seq_tup_read DESC;
|
|
||||||
|
|
||||||
-- 按照函数调用次数降序,查看所有存储过程调用情况
|
|
||||||
SELECT * FROM pg_stat_user_functions ORDER BY calls DESC;
|
|
||||||
|
|
||||||
-- 查看表使用的buffer
|
|
||||||
SELECT
|
|
||||||
relname,
|
|
||||||
count(*) AS buffers
|
|
||||||
FROM pg_class c
|
|
||||||
JOIN pg_buffercache b ON b.relfilenode = c.relfilenode
|
|
||||||
INNER JOIN pg_database d ON (b.reldatabase = d.oid AND d.datname = current_database())
|
|
||||||
WHERE c.relname NOT LIKE 'pg%'
|
|
||||||
GROUP BY c.relname
|
|
||||||
ORDER BY 2 DESC;
|
|
||||||
|
|
||||||
-- 检查表的脏页
|
|
||||||
SELECT
|
|
||||||
relname,
|
|
||||||
b.isdirty
|
|
||||||
FROM pg_class c
|
|
||||||
JOIN pg_buffercache b ON b.relfilenode = c.relfilenode
|
|
||||||
INNER JOIN pg_database d ON (b.reldatabase = d.oid AND d.datname = current_database())
|
|
||||||
WHERE c.relname NOT LIKE 'pg%'
|
|
||||||
ORDER BY 2 DESC;
|
|
||||||
```
|
|
||||||
|
|
@ -1,607 +0,0 @@
|
|||||||
---
|
|
||||||
author: "Vonng"
|
|
||||||
description: "PgBackRest中文文档"
|
|
||||||
categories: ["Ops"]
|
|
||||||
tags: ["PostgreSQL","pgBackrest"]
|
|
||||||
type: "post"
|
|
||||||
---
|
|
||||||
|
|
||||||
# pgBackRest文档
|
|
||||||
|
|
||||||
pgBackRest主页:http://pgbackrest.org
|
|
||||||
|
|
||||||
pgBackRest旨在提供一个简单可靠,容易纵向扩展的PostgreSQL备份恢复系统。
|
|
||||||
|
|
||||||
pgBackRest并不依赖像tar和rsync这样的传统备份工具,而在内部实现所有备份功能,并使用自定义协议来与远程系统进行通信。 消除对tar和rsync的依赖可以更好地解决特定于数据库的备份问题。 自定义远程协议提供了更多的灵活性,并限制执行备份所需的连接类型,从而提高安全性。
|
|
||||||
|
|
||||||
pgBackRest v1.27是目前的稳定版本。 发行说明位于发行版页面上。
|
|
||||||
|
|
||||||
## 0. 特性
|
|
||||||
|
|
||||||
* 并行备份和恢复
|
|
||||||
|
|
||||||
压缩通常是备份操作的瓶颈,但即使是现在已经很普及的多核服务器,大多数数据库备份解决方案仍然是单进程的。 pgBackRest通过并行处理解决了压缩瓶颈问题。利用多个核心进行压缩,即使在1Gb/s的链路上,也可以实现1TB /小时的原生吞吐量。更多的核心和更大的带宽将带来更高的吞吐量。
|
|
||||||
|
|
||||||
* 本地或远程操作
|
|
||||||
|
|
||||||
自定义协议允许pgBackRest以最少的配置通过SSH进行本地或远程备份,恢复和归档。通过协议层也提供了查询PostgreSQL的接口,从而不需要对PostgreSQL进行远程访问,从而增强了安全性。
|
|
||||||
|
|
||||||
* 全量备份与增量备份
|
|
||||||
|
|
||||||
支持全量备份,增量备份,以及差异备份。 pgBackRest不会受到rsync的时间分辨问题的影响,使得差异备份和增量备份完全安全。
|
|
||||||
|
|
||||||
* 备份轮换和归档过期
|
|
||||||
|
|
||||||
可以为全量备份和增量备份设置保留策略,以创建覆盖任何时间范围的备份。 WAL归档可以设置为为所有的备份或仅最近的备份保留。在后一种情况下,在归档过程中会自动保证更老备份的一致性。
|
|
||||||
|
|
||||||
* 备份完整性
|
|
||||||
|
|
||||||
每个文件在备份时都会计算校验和,并在还原过程中重新检查。完成文件复制后,备份会等待所有必须的WAL段进入存储库。存储库中的备份以与标准PostgreSQL集群(包括表空间)相同的格式存储。如果禁用压缩并启用硬链接,则可以在存储库中快照备份,并直接在快照上创建PostgreSQL集群。这对于以传统方式恢复很耗时的TB级数据库是有利的。所有操作都使用文件和目录级别fsync来确保持久性。
|
|
||||||
|
|
||||||
|
|
||||||
* 页面校验和
|
|
||||||
|
|
||||||
PostgreSQL从9.3开始支持页面级校验和。如果启用页面校验和,pgBackRest将验证在备份过程中复制的每个文件的校验和。所有页面校验和在完整备份过程中均得到验证,在差异备份和增量备份过程中验证了已更改文件中的校验和。
|
|
||||||
验证失败不会停止备份过程,但会向控制台和文件日志输出具体的哪些页面验证失败的详细警告。
|
|
||||||
|
|
||||||
此功能允许在包含有效数据副本的备份已过期之前及早检测到页级损坏。
|
|
||||||
|
|
||||||
* 备份恢复
|
|
||||||
|
|
||||||
中止的备份可以从停止点恢复。已经复制的文件将与清单中的校验和进行比较,以确保完整性。由于此操作可以完全在备份服务器上进行,因此减少了数据库服务器上的负载,并节省了时间,因为校验和计算比压缩和重新传输数据要快。
|
|
||||||
|
|
||||||
* 流压缩和校验和
|
|
||||||
|
|
||||||
无论存储库位于本地还是远程,压缩和校验和计算均在流中执行,而文件正在复制到存储库。
|
|
||||||
如果存储库位于备份服务器上,则在数据库服务器上执行压缩,并以压缩格式传输文件,并将其存储在备份服务器上。当禁用压缩时,利用较低级别的压缩来有效使用可用带宽,同时将CPU成本降至最低。
|
|
||||||
|
|
||||||
* 增量恢复
|
|
||||||
|
|
||||||
清单包含备份中每个文件的校验和,以便在还原过程中可以使用这些校验和来加快处理速度。在增量恢复时,备份中不存在的任何文件将首先被删除,然后对其余文件执行校验和。与备份相匹配的文件将保留在原位,其余文件将照常恢复。并行处理可能会导致恢复时间大幅减少。
|
|
||||||
|
|
||||||
* 并行WAL归档
|
|
||||||
|
|
||||||
包括专用的命令将WAL推送到归档并从归档中检索WAL。push命令会自动检测多次推送的WAL段,并在段相同时自动解除重复,否则会引发错误。 push和get命令都通过比较PostgreSQL版本和系统标识符来确保数据库和存储库匹配。这排除了错误配置WAL归档位置的可能性。
|
|
||||||
异步归档允许将传输转移到另一个并行压缩WAL段的进程,以实现最大的吞吐量。对于写入量非常高的数据库来说,这可能是一个关键功能。
|
|
||||||
|
|
||||||
* 表空间和链接支持
|
|
||||||
|
|
||||||
完全支持表空间,并且还原表空间可以重映射到任何位置。也可以使用一个对开发恢复有用的命令将所有的表空间重新映射到一个位置。
|
|
||||||
|
|
||||||
* Amazon S3支持
|
|
||||||
|
|
||||||
pgBackRest存储库可以存储在Amazon S3上,以实现几乎无限的容量和保留。
|
|
||||||
|
|
||||||
* 加密
|
|
||||||
|
|
||||||
pgBackRest可以对存储库进行加密,以保护无论存储在何处的备份。
|
|
||||||
|
|
||||||
* 与PostgreSQL兼容> = 8.3
|
|
||||||
|
|
||||||
pgBackRest包含了对8.3以下版本的支持,因为旧版本的PostgreSQL仍然是经常使用的。
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 1. 简介
|
|
||||||
|
|
||||||
本用户指南旨在从头到尾按顺序进行,每一节依赖上一节。例如“备份”部分依赖“快速入门”部分中执行的设置。
|
|
||||||
|
|
||||||
尽管这些例子是针对Debian / Ubuntu和PostgreSQL 9.4的,但是将这个指南应用到任何Unix发行版和PostgreSQL版本上应该相当容易。请注意,由于Perl代码中的64位操作,目前只支持64位发行版。唯一的特定于操作系统的命令是创建,启动,停止和删除PostgreSQL集群的命令。尽管安装Perl库和可执行文件的位置可能有所不同,但任何Unix系统上的pgBackRest命令都是相同的。
|
|
||||||
|
|
||||||
PostgreSQL的配置信息和文档可以在PostgreSQL手册中找到。
|
|
||||||
|
|
||||||
本用户指南采用了一些新颖的方法来记录。从XML源生成文档时,每个命令都在虚拟机上运行。这意味着您可以高度自信地确保命令按照所呈现的顺序正确工作。捕获输出并在适当的时候显示在命令之下。如果输出不包括,那是因为它被认为是不相关的或者被认为是从叙述中分心的。
|
|
||||||
|
|
||||||
所有的命令都是作为非特权用户运行的,它对root用户和postgres用户都具有sudo权限。也可以直接以各自的用户身份运行这些命令而不用修改,在这种情况下,sudo命令可以被剥离。
|
|
||||||
|
|
||||||
## 2. 概念
|
|
||||||
|
|
||||||
### 2.1 备份
|
|
||||||
|
|
||||||
备份是数据库集群的一致副本,可以从硬件故障中恢复,执行时间点恢复或启动新的备用数据库。
|
|
||||||
|
|
||||||
* 全量备份(Full Backup)
|
|
||||||
|
|
||||||
pgBackRest将数据库集簇的全部文件复制到备份服务器。数据库集簇的第一个备份总是全量备份。
|
|
||||||
|
|
||||||
pgBackRest总能从全量备份直接恢复。全量备份的一致性不依赖任何外部文件。
|
|
||||||
|
|
||||||
* 差异备份(Differential Backup)
|
|
||||||
|
|
||||||
pgBackRest仅复制自上次全量备份以来,内容发生变更的数据库群集文件。恢复时,pgBackRest拷贝差异备份中的所有文件,以及之前一次全量备份中所有未发生变更的文件。差异备份的优点是它比全量备份需要更少的硬盘空间,缺点是差异备份的恢复依赖上一次全量备份的有效性。
|
|
||||||
|
|
||||||
* 增量备份(Incremental Backup)
|
|
||||||
|
|
||||||
pgBackRest仅复制自上次备份(可能是另一个增量备份,差异备份或完全备份)以来发生更改的数据库群集文件。由于增量备份只包含自上次备份以来更改的那些文件,因此它们通常远远小于完全备份或差异备份。与差异备份一样,增量备份依赖于其他备份才能有效恢复增量备份。由于增量备份只包含自上次备份以来的文件,所有之前的增量备份都恢复到以前的差异,先前的差异备份和先前的完整备份必须全部有效才能执行增量备份的恢复。如果不存在差异备份,则以前的所有增量备份将恢复到之前的完整备份(必须存在),而完全备份本身必须有效才能恢复增量备份。
|
|
||||||
|
|
||||||
### 2.2 还原
|
|
||||||
|
|
||||||
还原是将备份复制到将作为实时数据库集群启动的系统的行为。还原需要备份文件和一个或多个WAL段才能正常工作。
|
|
||||||
|
|
||||||
#### 2.3 WAL
|
|
||||||
|
|
||||||
WAL是PostgreSQL用来确保没有提交的更改丢失的机制。将事务顺序写入WAL,并且在将这些写入刷新到磁盘时认为事务被提交。之后,后台进程将更改写入主数据库集群文件(也称为堆)。在发生崩溃的情况下,重播WAL以使数据库保持一致。
|
|
||||||
|
|
||||||
WAL在概念上是无限的,但在实践中被分解成单独的16MB文件称为段。 WAL段按照命名约定`0000000100000A1E000000FE`,其中前8个十六进制数字表示时间线,接下来的16个数字是逻辑序列号(LSN)。
|
|
||||||
|
|
||||||
#### 2.4 加密
|
|
||||||
|
|
||||||
加密是将数据转换为无法识别的格式的过程,除非提供了适当的密码(也称为密码短语)。
|
|
||||||
|
|
||||||
pgBackRest将根据用户提供的密码加密存储库,从而防止未经授权访问存储库中的数据。
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 3. 安装
|
|
||||||
|
|
||||||
### short version
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# cent-os
|
|
||||||
sudo yum install -y pgbackrest
|
|
||||||
|
|
||||||
# ubuntu
|
|
||||||
sudo apt-get install libdbd-pg-perl libio-socket-ssl-perl libxml-libxml-perl
|
|
||||||
```
|
|
||||||
|
|
||||||
### verbose version
|
|
||||||
|
|
||||||
创建一个名为db-primary的新主机来包含演示群集并运行pgBackRest示例。
|
|
||||||
如果已经安装了pgBackRest,最好确保没有安装先前的副本。取决于pgBackRest的版本可能已经安装在几个不同的位置。以下命令将删除所有先前版本的pgBackRest。
|
|
||||||
|
|
||||||
* db-primary⇒删除以前的pgBackRest安装
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo rm -f /usr/bin/pgbackrest
|
|
||||||
sudo rm -f /usr/bin/pg_backrest
|
|
||||||
sudo rm -rf /usr/lib/perl5/BackRest
|
|
||||||
sudo rm -rf /usr/share/perl5/BackRest
|
|
||||||
sudo rm -rf /usr/lib/perl5/pgBackRest
|
|
||||||
sudo rm -rf /usr/share/perl5/pgBackRest
|
|
||||||
```
|
|
||||||
|
|
||||||
pgBackRest是用Perl编写的,默认包含在Debian / Ubuntu中。一些额外的模块也必须安装,但是它们可以作为标准包使用。
|
|
||||||
|
|
||||||
* db-primary⇒安装必需的Perl软件包
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# cent-os
|
|
||||||
sudo yum install -y pgbackrest
|
|
||||||
|
|
||||||
# ubuntu
|
|
||||||
sudo apt-get install libdbd-pg-perl libio-socket-ssl-perl libxml-libxml-perl
|
|
||||||
```
|
|
||||||
|
|
||||||
适用于pgBackRest的Debian / Ubuntu软件包位于apt.postgresql.org。如果没有为您的发行版/版本提供,则可以轻松下载源代码并手动安装。
|
|
||||||
|
|
||||||
* db-primary⇒下载pgBackRest的1.27版本
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo wget -q -O - \
|
|
||||||
https://github.com/pgbackrest/pgbackrest/archive/release/1.27.tar.gz | \
|
|
||||||
sudo tar zx -C /root
|
|
||||||
```
|
|
||||||
|
|
||||||
* db-primary⇒安装pgBackRest
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo cp -r /root/pgbackrest-release-1.27/lib/pgBackRest \
|
|
||||||
/usr/share/perl5
|
|
||||||
sudo find /usr/share/perl5/pgBackRest -type f -exec chmod 644 {} +
|
|
||||||
sudo find /usr/share/perl5/pgBackRest -type d -exec chmod 755 {} +
|
|
||||||
sudo cp /root/pgbackrest-release-1.27/bin/pgbackrest /usr/bin/pgbackrest
|
|
||||||
sudo chmod 755 /usr/bin/pgbackrest
|
|
||||||
sudo mkdir -m 770 /var/log/pgbackrest
|
|
||||||
sudo chown postgres:postgres /var/log/pgbackrest
|
|
||||||
sudo touch /etc/pgbackrest.conf
|
|
||||||
sudo chmod 640 /etc/pgbackrest.conf
|
|
||||||
sudo chown postgres:postgres /etc/pgbackrest.conf
|
|
||||||
```
|
|
||||||
|
|
||||||
pgBackRest包含一个可选的伴随C库,可以增强性能并启用`checksum-page`选项和加密。预构建的软件包通常比手动构建C库更好,但为了完整性,下面给出了所需的步骤。根据分布情况,可能需要一些软件包,这里不一一列举。
|
|
||||||
|
|
||||||
* db-primary⇒构建并安装C库
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo sh -c 'cd /root/pgbackrest-release-1.27/libc && \
|
|
||||||
perl Makefile.PL INSTALLMAN1DIR=none INSTALLMAN3DIR=none'
|
|
||||||
sudo make -C /root/pgbackrest-release-1.27/libc test
|
|
||||||
sudo make -C /root/pgbackrest-release-1.27/libc install
|
|
||||||
```
|
|
||||||
|
|
||||||
现在pgBackRest应该正确安装了,但最好检查一下。如果任何依赖关系被遗漏,那么当你从命令行运行pgBackRest的时候你会得到一个错误。
|
|
||||||
|
|
||||||
* db-primary⇒确保安装正常
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo -u postgres pgbackrest
|
|
||||||
pgBackRest 1.27 - General help
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
pgbackrest [options] [command]
|
|
||||||
|
|
||||||
Commands:
|
|
||||||
archive-get Get a WAL segment from the archive.
|
|
||||||
archive-push Push a WAL segment to the archive.
|
|
||||||
backup Backup a database cluster.
|
|
||||||
check Check the configuration.
|
|
||||||
expire Expire backups that exceed retention.
|
|
||||||
help Get help.
|
|
||||||
info Retrieve information about backups.
|
|
||||||
restore Restore a database cluster.
|
|
||||||
stanza-create Create the required stanza data.
|
|
||||||
stanza-upgrade Upgrade a stanza.
|
|
||||||
start Allow pgBackRest processes to run.
|
|
||||||
stop Stop pgBackRest processes from running.
|
|
||||||
version Get version.
|
|
||||||
|
|
||||||
Use 'pgbackrest help [command]' for more information.
|
|
||||||
```
|
|
||||||
|
|
||||||
### mac version
|
|
||||||
|
|
||||||
在MacOS上安装可以按照之前的手动安装教程
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# install homebrew & wget
|
|
||||||
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
|
|
||||||
brew install wget
|
|
||||||
|
|
||||||
# install perl DB driver: Pg
|
|
||||||
perl -MCPAN -e 'install Bundle::DBD::Pg'
|
|
||||||
|
|
||||||
# Download and unzip
|
|
||||||
wget https://github.com/pgbackrest/pgbackrest/archive/release/1.27.tar.gz
|
|
||||||
|
|
||||||
# Copy to Perls lib
|
|
||||||
sudo cp -r ~/Downloads/pgbackrest-release-1.27/lib/pgBackRest /Library/Perl/5.18
|
|
||||||
sudo find /Library/Perl/5.18/pgBackRest -type f -exec chmod 644 {} +
|
|
||||||
sudo find /Library/Perl/5.18/pgBackRest -type d -exec chmod 755 {} +
|
|
||||||
|
|
||||||
# Copy binary to your path
|
|
||||||
sudo cp ~/Downloads/pgbackrest-release-1.27/bin/pgbackrest /usr/local/bin/
|
|
||||||
sudo chmod 755 /usr/local/bin/pgbackrest
|
|
||||||
|
|
||||||
# Make log dir & conf file. maybe you will change vonng to postgres
|
|
||||||
sudo mkdir -m 770 /var/log/pgbackrest && sudo touch /etc/pgbackrest.conf
|
|
||||||
sudo chmod 640 /etc/pgbackrest.conf
|
|
||||||
sudo chown vonng /etc/pgbackrest.conf /var/log/pgbackrest
|
|
||||||
|
|
||||||
# Uninstall
|
|
||||||
# sudo rm -rf /usr/local/bin/pgbackrest /Library/Perl/5.18/pgBackRest /var/log/pgbackrest /etc/pgbackrest.conf
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 4. 快速入门
|
|
||||||
|
|
||||||
### 4.1 搭建测试数据库集群
|
|
||||||
|
|
||||||
创建示例群集是可选的,但强烈建议试一遍,尤其对于新用户,因为用户指南中的示例命令引用了示例群集。 示例假定演示群集正在默认端口(即5432)上运行。直到后面的部分才会启动群集,因为还有一些配置要做。
|
|
||||||
|
|
||||||
* db-primary⇒创建演示群集
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# create database cluster
|
|
||||||
pg_ctl init -D /var/lib/pgsql/data
|
|
||||||
|
|
||||||
# change listen address to *
|
|
||||||
sed -ie "s/^#listen_addresses = 'localhost'/listen_addresses = '*'/g" /var/lib/pgsql/data/postgresql.conf
|
|
||||||
|
|
||||||
# change log prefix
|
|
||||||
sed -ie "s/^#log_line_prefix = '%m [%p] '/log_line_prefix = ''/g" /var/lib/pgsql/data/postgresql.conf
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
默认情况下PostgreSQL只接受本地连接。本示例需要来自其他服务器的连接,将listen_addresses配置为在所有端口上侦听。如果有安全性要求,这样做可能是不合适的。
|
|
||||||
|
|
||||||
出于演示目的,log_line_prefix设置将被最低限度地配置。这使日志输出尽可能简短,以更好地说明重要的信息。
|
|
||||||
|
|
||||||
### 4.2 配置集群的备份单元(Stanza)
|
|
||||||
|
|
||||||
一个备份单元是指 一组关于PostgreSQL数据库集簇的配置,它定义了数据库的位置,如何备份,归档选项等。大多数数据库服务器只有一个Postgres数据库集簇,因此只有一个备份单元,而备份服务器则对每一个需要备份的数据库集簇都有一个备份单元。
|
|
||||||
|
|
||||||
在主群集之后命名该节是诱人的,但是更好的名称描述群集中包含的数据库。由于节名称将用于主节点名称和所有副本,因此选择描述群集实际功能(例如app或dw)的名称(而不是本地群集名称(如main或prod))会更合适。
|
|
||||||
|
|
||||||
“Demo”这个名字可以准确地描述这个数据库集簇的目的,所以这里就这么用了。
|
|
||||||
|
|
||||||
`pgBackRest`需要知道PostgreSQL集簇的**数据目录**所在的位置。备份的时候PostgreSQL可以使用该目录,但恢复的时候PostgreSQL必须停机。备份期,提供给pgBackRest的值将与PostgreSQL运行的路径比较,如果它们不相等则备份将报错。确保`db-path`与`postgresql.conf`中的`data_directory`完全相同。
|
|
||||||
|
|
||||||
默认情况下,Debian / Ubuntu在/ var / lib / postgresql / [版本] / [集群]中存储集群,因此很容易确定数据目录的正确路径。
|
|
||||||
|
|
||||||
在创建`/etc/pgbackrest.conf`文件时,数据库所有者(通常是postgres)必须被授予读取权限。
|
|
||||||
|
|
||||||
* db-primary:`/etc/pgbackrest.conf`⇒配置PostgreSQL集群数据目录
|
|
||||||
|
|
||||||
```ini
|
|
||||||
[demo]
|
|
||||||
db-path=/var/lib/pgsql/data
|
|
||||||
```
|
|
||||||
|
|
||||||
pgBackRest配置文件遵循Windows INI约定。部分用括号中的文字表示,每个部分包含键/值对。以`#`开始的行被忽略,可以用作注释。
|
|
||||||
|
|
||||||
### 4.3 创建存储库
|
|
||||||
|
|
||||||
存储库是pgBackRest存储备份和归档WAL段的地方。
|
|
||||||
|
|
||||||
新备份很难提前估计需要多少空间。最好的办法是做一些备份,然后记录不同类型备份的大小(full / incr / diff),并测量每天产生的WAL数量。这将给你一个大致需要多少空间的概念。当然随着数据库的发展,需求可能会随着时间而变化。
|
|
||||||
|
|
||||||
对于这个演示,存储库将被存储在与PostgreSQL服务器相同的主机上。这是最简单的配置,在使用传统备份软件备份数据库主机的情况下非常有用。
|
|
||||||
|
|
||||||
* db-primary⇒创建pgBackRest存储库
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo mkdir /var/lib/pgbackrest
|
|
||||||
sudo chmod 750 /var/lib/pgbackrest
|
|
||||||
sudo chown postgres:postgres /var/lib/pgbackrest
|
|
||||||
```
|
|
||||||
|
|
||||||
存储库路径必须配置,以便pgBackRest知道在哪里找到它。
|
|
||||||
|
|
||||||
* db-primary:`/etc/pgbackrest.conf` ⇒配置pgBackRest存储库路径
|
|
||||||
|
|
||||||
```ini
|
|
||||||
[demo]
|
|
||||||
db-path=/var/lib/postgresql/9.4/demo
|
|
||||||
|
|
||||||
[global]
|
|
||||||
repo-path=/var/lib/pgbackrest
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 4.4 配置归档
|
|
||||||
|
|
||||||
备份正在运行的PostgreSQL集群需要启用WAL归档。请注意,即使没有对群集进行明确写入,在备份过程中也会创建至少一个WAL段。
|
|
||||||
|
|
||||||
* db-primary:`/var/lib/pgsql/data/postgresql.conf`⇒ 配置存档设置
|
|
||||||
|
|
||||||
```ini
|
|
||||||
archive_command = 'pgbackrest --stanza=demo archive-push %p'
|
|
||||||
archive_mode = on
|
|
||||||
listen_addresses = '*'
|
|
||||||
log_line_prefix = ''
|
|
||||||
max_wal_senders = 3
|
|
||||||
wal_level = hot_standby
|
|
||||||
```
|
|
||||||
|
|
||||||
wal_level设置必须至少设置为`archive`,但`hot_standby`和`logical`也适用于备份。 在PostgreSQL 10中,相应的wal_level是`replica`。将wal_level设置为hot_standy并增加max_wal_senders是一个好主意,即使您当前没有运行热备用数据库也是一个好主意,因为这样可以在不重新启动主群集的情况下添加它们。在进行这些更改之后和执行备份之前,必须重新启动PostgreSQL群集。
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 4.5 保留配置(retention)
|
|
||||||
|
|
||||||
pgBackRest会根据保留配置对备份进行过期处理。
|
|
||||||
|
|
||||||
* db-primary: `/etc/pgbackrest.conf` ⇒ 配置为保留两个全量备份
|
|
||||||
|
|
||||||
```ini
|
|
||||||
[demo]
|
|
||||||
db-path=/var/lib/postgresql/9.4/demo
|
|
||||||
|
|
||||||
[global]
|
|
||||||
repo-path=/var/lib/pgbackrest
|
|
||||||
|
|
||||||
retention-full=2
|
|
||||||
```
|
|
||||||
|
|
||||||
更多关于保留的信息可以在`Retention`一节找到。
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 4.6 配置存储库加密
|
|
||||||
|
|
||||||
该节创建命令必须在仓库位于初始化节的主机上运行。建议的检查命令后运行节创建,确保归档和备份的配置是否正确。
|
|
||||||
|
|
||||||
* db-primary: `/etc/pgbackrest.conf` ⇒ 配置pgBackRest存储库加密
|
|
||||||
|
|
||||||
```ini
|
|
||||||
[demo]
|
|
||||||
db-path=/var/lib/postgresql/9.4/demo
|
|
||||||
|
|
||||||
[global]
|
|
||||||
repo-cipher-pass=zWaf6XtpjIVZC5444yXB+cgFDFl7MxGlgkZSaoPvTGirhPygu4jOKOXf9LO4vjfO
|
|
||||||
repo-cipher-type=aes-256-cbc
|
|
||||||
repo-path=/var/lib/pgbackrest
|
|
||||||
retention-full=2
|
|
||||||
```
|
|
||||||
|
|
||||||
一旦存储库(repository)配置完成且备份单元创建并检查完毕,存储库加密设置便不能更改。
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 4.7 创建存储单元
|
|
||||||
|
|
||||||
`stanza-create`命令必须在仓库位于初始化节的主机上运行。建议在`stanza-create`命令之后运行`check`命令,确保归档和备份的配置是否正确。
|
|
||||||
|
|
||||||
* db-primary ⇒ 创建存储单元并检查配置
|
|
||||||
|
|
||||||
```bash
|
|
||||||
postgres$ pgbackrest --stanza=demo --log-level-console=info stanza-create
|
|
||||||
|
|
||||||
P00 INFO: stanza-create command begin 1.27: --db1-path=/var/lib/postgresql/9.4/demo --log-level-console=info --no-log-timestamp --repo-cipher-pass= --repo-cipher-type=aes-256-cbc --repo-path=/var/lib/pgbackrest --stanza=demo
|
|
||||||
|
|
||||||
P00 INFO: stanza-create command end: completed successfully
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
```
|
|
||||||
1. Install
|
|
||||||
|
|
||||||
$ sudo yum install -y pgbackrest
|
|
||||||
|
|
||||||
|
|
||||||
2. configuration
|
|
||||||
|
|
||||||
1) pgbackrest.conf
|
|
||||||
|
|
||||||
$ sudo vim /etc/pgbackrest.conf
|
|
||||||
[global]
|
|
||||||
repo-cipher-pass=O8lotSfiXYSYomc9BQ0UzgM9PgXoyNo1t3c0UmiM7M26rOETVNawbsW7BYn+I9es
|
|
||||||
repo-cipher-type=aes-256-cbc
|
|
||||||
repo-path=/var/backups
|
|
||||||
retention-full=2
|
|
||||||
retention-diff=2
|
|
||||||
retention-archive=2
|
|
||||||
start-fast=y
|
|
||||||
stop-auto=y
|
|
||||||
archive-copy=y
|
|
||||||
|
|
||||||
[global:archive-push]
|
|
||||||
archive-async=y
|
|
||||||
process-max=4
|
|
||||||
|
|
||||||
[test]
|
|
||||||
db-path=/var/lib/pgsql/9.5/data
|
|
||||||
process-max=10
|
|
||||||
|
|
||||||
2) postgresql.conf
|
|
||||||
|
|
||||||
$ sudo vim /var/lib/pgsql/9.5/data/postgresql.conf
|
|
||||||
archive_command = '/usr/bin/pgbackrest --stanza=test archive-push %p'
|
|
||||||
|
|
||||||
3. Initial
|
|
||||||
|
|
||||||
$ sudo chown -R postgres:postgres /var/backups/
|
|
||||||
$ sudo -u postgres pgbackrest --stanza=test --log-level-console=info stanza-create
|
|
||||||
2018-01-04 11:38:21.082 P00 INFO: stanza-create command begin 1.27: --db1-path=/var/lib/pgsql/9.5/data --log-level-console=info --repo-cipher-pass=<redacted> --repo-cipher-type=aes-256-cbc --repo-path=/var/backups --stanza=test
|
|
||||||
2018-01-04 11:38:21.533 P00 INFO: stanza-create command end: completed successfully
|
|
||||||
$ sudo service postgresql-9.5 reload
|
|
||||||
|
|
||||||
$ sudo -u postgres pgbackrest --stanza=test --log-level-console=info info
|
|
||||||
stanza: test
|
|
||||||
status: error (no valid backups)
|
|
||||||
|
|
||||||
db (current)
|
|
||||||
wal archive min/max (9.5-1): 0000000500041CFD000000BE / 0000000500041CFD000000BE
|
|
||||||
|
|
||||||
4. Backup
|
|
||||||
|
|
||||||
$ sudo -u postgres pgbackrest --stanza=test --log-level-console=info --type=full backup
|
|
||||||
2018-01-04 16:24:57.329 P00 INFO: backup command begin 1.27: --archive-copy --db1-path=/var/lib/pgsql/9.5/data --log-level-console=info --process-max=40 --repo-cipher-pass=<redacted> --repo-cipher-type=aes-
|
|
||||||
256-cbc --repo-path=/var/backups --retention-archive=2 --retention-diff=2 --retention-full=2 --stanza=test --start-fast --stop-auto --type=full
|
|
||||||
2018-01-04 16:24:58.192 P00 INFO: execute exclusive pg_start_backup() with label "pgBackRest backup started at 2018-01-04 16:24:57": backup begins after the requested immediate checkpoint completes
|
|
||||||
2018-01-04 16:24:58.495 P00 INFO: backup start archive = 0000000500041CFD000000C0, lsn = 41CFD/C0000060
|
|
||||||
2018-01-04 16:26:04.863 P34 INFO: backup file /var/lib/pgsql/9.5/data/base/16384/3072016471.83 (1GB, 0%) checksum ab17fdd9f70652a0de55fd0da5d2b6b1f48de490
|
|
||||||
2018-01-04 16:26:04.923 P35 INFO: backup file /var/lib/pgsql/9.5/data/base/16384/3072016471.82 (1GB, 0%) checksum 5acba8d0eb70dcdc64199201ee3999743e747699
|
|
||||||
2018-01-04 16:26:05.208 P37 INFO: backup file /var/lib/pgsql/9.5/data/base/16384/3072016471.80 (1GB, 0%) checksum 74e2f876d8e7d68ab29624d53d33b0c6cb078382
|
|
||||||
2018-01-04 16:26:06.973 P30 INFO: backup file /var/lib/pgsql/9.5/data/base/16384/3072016471.87 (1GB, 1%) checksum b6d6884724178476ee24a9a1a812e8941d4da396
|
|
||||||
2018-01-04 16:26:09.434 P24 INFO: backup file /var/lib/pgsql/9.5/data/base/16384/3072016471.92 (1GB, 1%) checksum c5e6232171e0a7cadc7fc57f459a7bc75c2955d8
|
|
||||||
2018-01-04 16:26:09.860 P40 INFO: backup file /var/lib/pgsql/9.5/data/base/16384/3072016471.78 (1GB, 1%) checksum 95d94b1bac488592677f7942b85ab5cc2a39bf62
|
|
||||||
2018-01-04 16:26:10.708 P33 INFO: backup file /var/lib/pgsql/9.5/data/base/16384/3072016471.84 (1GB, 2%) checksum 32e8c83f9bdc5934552f54ee59841f1877b04f69
|
|
||||||
2018-01-04 16:26:11.035 P28 INFO: backup file /var/lib/pgsql/9.5/data/base/16384/3072016471.89 (1GB, 2%) checksum aa7bee244d2d2c49b56bc9b2e0b9bf36f2bcc227
|
|
||||||
2018-01-04 16:26:11.239 P17 INFO: backup file /var/lib/pgsql/9.5/data/base/16384/3072016471.99 (1GB, 2%) checksum 218bcecf7da2230363926ca00d719011a6c27467
|
|
||||||
2018-01-04 16:26:11.383 P18 INFO: backup file /var/lib/pgsql/9.5/data/base/16384/3072016471.98 (1GB, 2%) checksum 38744d27867017dfadb6b520b6c0034daca67481
|
|
||||||
...
|
|
||||||
2018-01-04 16:34:07.782 P32 INFO: backup file /var/lib/pgsql/9.5/data/base/16384/3072016471.184 (852.7MB, 98%) checksum 92990e159b0436d5a6843d21b2d888b636e246cf
|
|
||||||
2018-01-04 16:34:07.935 P10 INFO: backup file /var/lib/pgsql/9.5/data/base/16384/3072016468.100 (1GB, 98%) checksum d9e0009447a5ef068ce214239f1c999cc5251462
|
|
||||||
2018-01-04 16:34:10.212 P35 INFO: backup file /var/lib/pgsql/9.5/data/base/16384/3072016476.3 (569.6MB, 98%) checksum d02e6efed6cea3005e1342d9d6a8e27afa5239d7
|
|
||||||
2018-01-04 16:34:12.289 P20 INFO: backup file /var/lib/pgsql/9.5/data/base/16384/3072016468.10 (1GB, 98%) checksum 1a99468cd18e9399ade9ddc446eb21f1c4a1f137
|
|
||||||
2018-01-04 16:34:13.270 P03 INFO: backup file /var/lib/pgsql/9.5/data/base/16384/3072016468.1 (1GB, 99%) checksum c0ddb80d5f1be83aa4557777ad05adb7cbc47e72
|
|
||||||
2018-01-04 16:34:13.792 P38 INFO: backup file /var/lib/pgsql/9.5/data/base/16384/3072016468 (1GB, 99%) checksum 767a2e0d21063b92b9cebc735fbb0e3c7332218d
|
|
||||||
2018-01-04 16:34:18.446 P26 INFO: backup file /var/lib/pgsql/9.5/data/base/16384/3072016473.3 (863.9MB, 99%) checksum 87ba54690ea418c2ddd1d488c56fa164ebda5042
|
|
||||||
2018-01-04 16:34:23.551 P13 INFO: backup file /var/lib/pgsql/9.5/data/base/16384/3072016475.7 (895.4MB, 100%) checksum a2693bfdc84940c82b7d77a13b752e33448bb008
|
|
||||||
2018-01-04 16:34:23.648 P00 INFO: full backup size = 341.5GB
|
|
||||||
2018-01-04 16:34:23.649 P00 INFO: execute exclusive pg_stop_backup() and wait for all WAL segments to archive
|
|
||||||
2018-01-04 16:34:37.774 P00 INFO: backup stop archive = 0000000500041CFD000000C0, lsn = 41CFD/C0000168
|
|
||||||
2018-01-04 16:34:39.648 P00 INFO: new backup label = 20180104-162457F
|
|
||||||
2018-01-04 16:34:41.004 P00 INFO: backup command end: completed successfully
|
|
||||||
2018-01-04 16:34:41.005 P00 INFO: expire command begin 1.27: --log-level-console=info --repo-cipher-pass=<redacted> --repo-cipher-type=aes-256-cbc --repo-path=/var/backups --retention-archive=2 --retention-diff=2 --retention-full=2 --stanza=test
|
|
||||||
2018-01-04 16:34:41.028 P00 INFO: full backup total < 2 - using oldest full backup for 9.5-1 archive retention
|
|
||||||
2018-01-04 16:34:41.034 P00 INFO: expire command end: completed successfully
|
|
||||||
|
|
||||||
$ sudo -u postgres pgbackrest --stanza=test --log-level-console=info info
|
|
||||||
stanza: test
|
|
||||||
status: ok
|
|
||||||
|
|
||||||
db (current)
|
|
||||||
wal archive min/max (9.5-1): 0000000500041CFD000000C0 / 0000000500041CFD000000C0
|
|
||||||
|
|
||||||
full backup: 20180104-162457F
|
|
||||||
timestamp start/stop: 2018-01-04 16:24:57 / 2018-01-04 16:34:38
|
|
||||||
wal start/stop: 0000000500041CFD000000C0 / 0000000500041CFD000000C0
|
|
||||||
database size: 341.5GB, backup size: 341.5GB
|
|
||||||
repository size: 153.6GB, repository backup size: 153.6GB
|
|
||||||
|
|
||||||
|
|
||||||
5. restore
|
|
||||||
|
|
||||||
$ sudo vim /etc/pgbackrest.conf
|
|
||||||
db-path=/export/pgdata
|
|
||||||
$ sudo mkdir /export/pgdata
|
|
||||||
$ sudo chown -R postgres:postgres /export/pgdata/
|
|
||||||
$ sudo chmod 0700 /export/pgdata/
|
|
||||||
$ sudo -u postgres pgbackrest --stanza=test --log-level-console=info --delta --set=20180104-162457F --type=time "--target=2018-01-04 16:34:38" restore
|
|
||||||
2018-01-04 17:04:23.170 P00 INFO: restore command begin 1.27: --db1-path=/export/pgdata --delta --log-level-console=info --process-max=40 --repo-cipher-pass=<redacted> --repo-cipher-type=aes-256-cbc --repo-
|
|
||||||
path=/var/backups --set=20180104-162457F --stanza=test "--target=2018-01-04 16:34:38" --type=time
|
|
||||||
WARN: --delta or --force specified but unable to find 'PG_VERSION' or 'backup.manifest' in '/export/pgdata' to confirm that this is a valid $PGDATA directory. --delta and --force have been disabled and if an
|
|
||||||
y files exist in the destination directories the restore will be aborted.
|
|
||||||
2018-01-04 17:04:23.313 P00 INFO: restore backup set 20180104-162457F
|
|
||||||
2018-01-04 17:04:23.935 P00 INFO: remap $PGDATA directory to /export/pgdata
|
|
||||||
2018-01-04 17:05:09.626 P01 INFO: restore file /export/pgdata/base/16384/3072016476.2 (1GB, 0%) checksum be1145405b8bcfa57c3f1fd8d0a78eee3ed2df21
|
|
||||||
2018-01-04 17:05:09.627 P04 INFO: restore file /export/pgdata/base/16384/3072016475.6 (1GB, 0%) checksum d2bc51d5b58dea3d14869244cd5a23345dbc4ffb
|
|
||||||
2018-01-04 17:05:09.627 P27 INFO: restore file /export/pgdata/base/16384/3072016471.9 (1GB, 0%) checksum 94cbf743143baffac0b1baf41e60d4ed99ab910f
|
|
||||||
2018-01-04 17:05:09.627 P37 INFO: restore file /export/pgdata/base/16384/3072016471.80 (1GB, 1%) checksum 74e2f876d8e7d68ab29624d53d33b0c6cb078382
|
|
||||||
2018-01-04 17:05:09.627 P38 INFO: restore file /export/pgdata/base/16384/3072016471.8 (1GB, 1%) checksum 5f0edd85543c9640d2c6cf73257165e621a6b295
|
|
||||||
2018-01-04 17:05:09.652 P02 INFO: restore file /export/pgdata/base/16384/3072016476.1 (1GB, 1%) checksum 3e262262b106bdc42c9fe17ebdf62bc4ab2e8166
|
|
||||||
...
|
|
||||||
2018-01-04 17:09:15.415 P34 INFO: restore file /export/pgdata/base/1/13142 (0B, 100%)
|
|
||||||
2018-01-04 17:09:15.415 P35 INFO: restore file /export/pgdata/base/1/13137 (0B, 100%)
|
|
||||||
2018-01-04 17:09:15.415 P36 INFO: restore file /export/pgdata/base/1/13132 (0B, 100%)
|
|
||||||
2018-01-04 17:09:15.415 P37 INFO: restore file /export/pgdata/base/1/13127 (0B, 100%)
|
|
||||||
2018-01-04 17:09:15.418 P00 INFO: write /export/pgdata/recovery.conf
|
|
||||||
2018-01-04 17:09:15.950 P00 INFO: restore global/pg_control (performed last to ensure aborted restores cannot be started)
|
|
||||||
2018-01-04 17:09:16.588 P00 INFO: restore command end: completed successfully
|
|
||||||
|
|
||||||
$ sudo vim /export/pgdata/postgresql.conf
|
|
||||||
port = 5433
|
|
||||||
$ sudo -u postgres /usr/pgsql-9.5/bin/pg_ctl -D /export/pgdata/ start
|
|
||||||
server starting
|
|
||||||
< 2018-01-04 17:13:47.361 CST >LOG: redirecting log output to logging collector process
|
|
||||||
< 2018-01-04 17:13:47.361 CST >HINT: Future log output will appear in directory "pg_log".
|
|
||||||
|
|
||||||
$ sudo -u postgres psql -p5433
|
|
||||||
psql (9.5.10)
|
|
||||||
Type "help" for help.
|
|
||||||
|
|
||||||
postgres=# \q
|
|
||||||
|
|
||||||
6. archive_command and restore_command
|
|
||||||
1) on master
|
|
||||||
$ sudo vim /var/lib/pgsql/9.5/data/postgresql.conf
|
|
||||||
archive_command = '/usr/bin/pgbackrest --stanza=test archive-push %p'
|
|
||||||
$ sudo service postgresql-9.5 reload
|
|
||||||
$ sudo yum install -y -q nfs-utils
|
|
||||||
$ sudo echo "/var/backups 10.191.0.0/16(rw)" > /etc/exports
|
|
||||||
$ sudo service nfs start
|
|
||||||
|
|
||||||
2) on slave
|
|
||||||
$ sudo mount -o v3 master_ip:/var/backups /var/backups
|
|
||||||
$ sudo vim /etc/pgbackrest.conf
|
|
||||||
[global]
|
|
||||||
repo-cipher-pass=O8lotSfiXYSYomc9BQ0UzgM9PgXoyNo1t3c0UmiM7M26rOETVNawbsW7BYn+I9es
|
|
||||||
repo-cipher-type=aes-256-cbc
|
|
||||||
repo-path=/var/backups
|
|
||||||
retention-full=2
|
|
||||||
retention-diff=2
|
|
||||||
retention-archive=2
|
|
||||||
start-fast=y
|
|
||||||
stop-auto=y
|
|
||||||
archive-copy=y
|
|
||||||
|
|
||||||
[global:archive-push]
|
|
||||||
archive-async=y
|
|
||||||
process-max=4
|
|
||||||
|
|
||||||
[test]
|
|
||||||
db-path=/var/lib/pgsql/9.5/data
|
|
||||||
process-max=10
|
|
||||||
|
|
||||||
$ sudo vim /var/lib/pgsql/9.5/data/recovery.conf
|
|
||||||
restore_command = '/usr/bin/pgbackrest --stanza=test archive-get %f "%p"'
|
|
||||||
|
|
||||||
|
|
||||||
```
|
|
@ -1,77 +0,0 @@
|
|||||||
# Setup
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 1. PostgreSQL用户账户
|
|
||||||
|
|
||||||
* 使用一个独立的用户下运行PostgreSQL,不与其他程序共享。
|
|
||||||
* 默认用户是`postgres`
|
|
||||||
* 使用`adduser`或`useradd`添加用户。
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 2. 创建数据库组
|
|
||||||
|
|
||||||
使用数据库前要在磁盘上初始化一个数据库存储区域。称之为一个*数据库集簇(database cluster)*,SQL标准术语为*目录集簇 catelog cluster*。一个数据库集簇被一个Pg Server实例管理,是多个数据库的集合。
|
|
||||||
|
|
||||||
在初始化之后,一个数据库集簇将包含一个名为`postgres`的默认数据库,一个名为`template1`的模板数据库。
|
|
||||||
|
|
||||||
### 数据目录
|
|
||||||
|
|
||||||
从文件系统的视角看,一个数据库集簇是一个单一目录,所有数据都将被存储在其中。称之为*数据目录(data directory)*或*数据区域(data area)*。
|
|
||||||
|
|
||||||
#### 位置选择
|
|
||||||
|
|
||||||
* 数据目录没有默认的位置,通常使用`/usr/local/pgsql/data`或`/var/lib/pgsql/data`。
|
|
||||||
* 通常数据库目录名为`data`,用户`postgres`至少需要拥有该目录及父目录的写权限。
|
|
||||||
* 不要使用挂载点直接作为数据目录,以免一堆权限问题。
|
|
||||||
* 不要软挂载NFS,有各种烂问题。
|
|
||||||
|
|
||||||
#### 初始化
|
|
||||||
|
|
||||||
使用`initdb`,通过`-D`选项或环境变量`PGDATA`指定数据目录的位置:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ initdb -D /usr/local/pgsql/data
|
|
||||||
```
|
|
||||||
|
|
||||||
另一种替代方案是`pg_ctl`,实际上调用了`initdb`
|
|
||||||
|
|
||||||
```
|
|
||||||
$ pg_ctl -D /usr/local/pgsql/data initdb
|
|
||||||
```
|
|
||||||
|
|
||||||
* 如果目标目录不存在,将会创建。
|
|
||||||
* 如果目标目录已存在或没有父目录的写入权限,将会失败
|
|
||||||
* 初始化会回收其他用户的访问权限。
|
|
||||||
* 使用`-W`选项为超级用户设置一个初始密码。
|
|
||||||
* 默认使用系统的`Locale`和字符集编码。使用`--locale`指明区域,使用`-E`指明编码
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 3. 启动数据库服务器
|
|
||||||
|
|
||||||
* 服务器程序是`postgres`,位于`$PGHOME/bin/postgres`。
|
|
||||||
* 至少需要通过`-D`或`PGDATA`提供数据目录的地址,否则启动失败。
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 前台启动
|
|
||||||
postgres -D /var/lib/pgsql/data
|
|
||||||
# 后台启动
|
|
||||||
postgres -D /var/lib/pgsql/data >logfile 2>&1 &
|
|
||||||
```
|
|
||||||
|
|
||||||
* 更好的方式是通过`pg_ctl`来启动服务
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pg_ctl -l logfile
|
|
||||||
```
|
|
||||||
|
|
||||||
* 日志文件自己指定存放位置,使用分割工具做log rotate
|
|
||||||
* 启动Server时会在数据目录写入`postmaster.pid`文件。
|
|
||||||
|
|
||||||
### 开机自动启动
|
|
||||||
|
|
||||||
* `contrib/start-scripts` 提供了操作系统相关的开机自动启动脚本,需要root权限安装。
|
|
@ -1,121 +0,0 @@
|
|||||||
---
|
|
||||||
author: "Vonng"
|
|
||||||
description: "PostgreSQL WAL与检查点"
|
|
||||||
categories: ["DBA"]
|
|
||||||
tags: ["PostgreSQL","WAL", "Internal"]
|
|
||||||
type: "post"
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# PostgreSQL WAL & Checkpoint
|
|
||||||
|
|
||||||
数据库需要保证两个基本的特性:**可靠性**与**可用性**。通俗来讲:
|
|
||||||
|
|
||||||
可靠性就是:出了故障,既不会丢数据,也不会弄脏数据。
|
|
||||||
|
|
||||||
可用性就是:保证足够的读写性能,出了故障后,能够快速恢复服务。
|
|
||||||
|
|
||||||
朴素的数据库实现有两个选项:在内存中修改数据页,或者将事物变更直接写入磁盘。但这产生了一个两难困境:
|
|
||||||
|
|
||||||
* 内存支持随机读写,因此在性能上表现强悍,然而作为易失性存储,一旦故障就会丢数据。
|
|
||||||
* 硬盘恰恰相反,随机读写表现糟糕,但在故障时数据要可靠的多。
|
|
||||||
|
|
||||||
内存可用性强可靠性差,硬盘可用性差但可靠性强,如何解决这一对矛盾,让内存与硬盘取长补短,就是生产级数据库需要考虑的问题了。
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 0x1 核心思想
|
|
||||||
|
|
||||||
硬盘的随机写入性能很糟糕,但顺序写入的性能却非常可观。即使是SSD也符合这一规律,因为一次写入的擦除单位是Block(通常是几M),而操作系统的写入单元是Page(通常约4k)。如果每次事物提交都要直接将脏数据页落盘,性能表现肯定不会可观。但如果采用另一种方式,将**数据的变更**而不是**变更后的最新数据本身**落盘,就可以将随机写入变为顺序写入,从而极大地提高磁盘写入效率。
|
|
||||||
|
|
||||||
于是,预写式日志(WAL,Write Ahead Log) 出现了,所谓日志,在最朴素的意义上来讲,就是一个Append-Only的数据文件,记录了操作的内容。只要保留了WAL,数据库就是可靠的,可以恢复的。从一个给定的状态,例如空数据库开始,回放所有的操作日志到当前的时间点,就可以恢复出当前数据库应有的状态。与此同时,如果日志已经落盘确保了可靠性,数据页就不需要在每次提交时落盘了。数据页的读写可以完全在内存进行,从而提供强悍的性能支持。
|
|
||||||
|
|
||||||
**但可用性不仅仅包括足够的性能,当发生故障时能够快速恢复也是可用性要求的一部分**。考虑最极端的情况,从数据库创建之初所有数据页就在内存里一直飘着,只有操作日志落了盘。现在数据库运行了一整年,突然崩溃了,这时候要想恢复就需要重放一整年的操作日志,也许需要几个小时,也许需要好几天。对于生产环境,这是无法接受的,检查点(Checkpoint)解决了这个问题。
|
|
||||||
|
|
||||||
检查点(Checkpoint)类似于游戏中存档的概念,远古时期的很多游戏没有存档,一旦Game Over就要重头再来。后来的游戏有了记忆和存档,当挑战Boss失败时,只要读取最近的存档,就可以避免从头开始。
|
|
||||||
|
|
||||||
数据库中的检查点代表这样一种操作,在某一个检查点时,所有脏数据页会写回到磁盘中,使得磁盘和内存中的数据保持一致。这样当故障恢复时,只需要从该检查点开始回放操作日志即可。
|
|
||||||
|
|
||||||
例如,每个整点执行一次检查点,存档一次,那么当故障时,只需要从本小时开始的检查点开始回放WAL,就可以完成恢复。同时,检查点还有一个好处是,当数据页落盘之后,在这个检查点之前的WAL日志就可以不用了。对于高负载数据库,例如每小时产生TB级别WAL的数据库,使用检查点能够极大地减少恢复的时间和磁盘的用量。
|
|
||||||
|
|
||||||
通过检查点和预写式日志,数据库可以同时保证高度的可靠性和可用性。
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 0x2 WAL概述
|
|
||||||
|
|
||||||
预写式日志(WAL)是保证数据完整性的一种标准方法。对其详尽的描述几乎可以在所有(如果不是全部)有关事务处理的书中找到。简单来说,WAL的中心概念是数据文件(存储着表和索引)的修改必须在这些动作被日志记录之后才被写入,即在描述这些改变的日志记录被刷到持久存储以后。如果我们遵循这种过程,我们不需要在每个事务提交时刷写数据页面到磁盘,因为我们知道在发生崩溃时可以使用日志来恢复数据库:任何还没有被应用到数据页面的改变可以根据其日志记录重做(这是前滚恢复,也被称为REDO)。
|
|
||||||
|
|
||||||
使用WAL可以显著降低磁盘的写次数,因为只有日志文件需要被刷出到磁盘以保证事务被提交,而被事务改变的每一个数据文件则不必被刷出。**日志文件被按照顺序写入,因此同步日志的代价要远低于刷写数据页面的代价**。在处理很多影响数据存储不同部分的小事务的服务器上这一点尤其明显。此外,当服务器在处理很多小的并行事务时,日志文件的一个`fsync`可以提交很多事务。
|
|
||||||
|
|
||||||
##异步提交
|
|
||||||
|
|
||||||
*异步提交*是一个允许事务能更快完成的选项,代价是在数据库崩溃时最近的事务会丢失。在很多应用中这是一个可接受的交换。
|
|
||||||
|
|
||||||
如前一节所述,事务提交通常是*同步的*:服务器等到事务的WAL记录被刷写到持久存储之后才向客户端返回成功指示。因此客户端可以确保那些报告已被提交的事务确会被保存,即便随后马上发生了一次服务器崩溃。但是,对于短事务来说这种延迟是其总执行时间的主要部分。选择异步提交模式意味着服务器将在事务被逻辑上提交后立刻返回成功,而此时由它生成的WAL记录还没有被真正地写到磁盘上。这将为小型事务的生产力产生显著地提升。
|
|
||||||
|
|
||||||
异步提交会带来数据丢失的风险。在向客户端报告事务完成到事务真正被提交(即能保证服务器崩溃时它也不会被丢失)之间有一个短的时间窗口。因此如果客户端将会做一些要求其事务被记住的外部动作,就不应该用异步提交。例如,一个银行肯定不会使用异步提交事务来记录一台ATM的现金分发。但是在很多情境中不需要这种强的保证,例如事件日志。
|
|
||||||
|
|
||||||
使用异步提交带来的风险是数据丢失,而不是数据损坏。如果数据库可能崩溃,它会通过重放WAL到被刷写的最后一个记录来进行恢复。数据库将因此被恢复到一个自身一致状态,但是任何还没有被刷写到磁盘的事务将不会反映在该状态中。因此其影响就是丢失了最后的少量事务。由于事务按照提交顺序被重放,所以不会出现任何不一致性 — 例如一个事务B按照前面一个事务A的效果来进行修改,则不会出现A的效果丢失而B的效果被保留的情况。
|
|
||||||
|
|
||||||
用户可以选择每一个事务的提交模式,这样可以有同步提交和异步提交的事务并行运行。这允许我们灵活地在性能和事务持久性之间进行权衡。提交模式由用户可设置的参数[synchronous_commit](http://www.postgres.cn/docs/9.6/runtime-config-wal.html#GUC-SYNCHRONOUS-COMMIT)控制,它可以使用任何一种修改配置参数的方法进行设置。一个事务真正使用的提交模式取决于当事务提交开始时`synchronous_commit`的值。
|
|
||||||
|
|
||||||
特定的实用命令,如`DROP TABLE`,被强制按照同步提交而不考虑`synchronous_commit`的设定。这是为了确保服务器文件系统和数据库逻辑状态之间的一致性。支持两阶段提交的命令页总是同步提交的,如`PREPARE TRANSACTION`。
|
|
||||||
|
|
||||||
如果数据库在异步提交和事务WAL记录写入之间的风险窗口期间崩溃,在该事务期间所作的修改*将*丢失。风险窗口的持续时间是有限制的,因为一个后台进程("WAL写进程")每[wal_writer_delay](http://www.postgres.cn/docs/9.6/runtime-config-wal.html#GUC-WAL-WRITER-DELAY)毫秒会把未写入的WAL记录刷写到磁盘。风险窗口实际的最大持续时间是`wal_writer_delay`的3倍,因为WAL写进程被设计成倾向于在忙时一次写入所有页面。
|
|
||||||
|
|
||||||
一个立刻关闭等同于一次服务器崩溃,因此也将会导致未刷写的异步提交丢失。
|
|
||||||
|
|
||||||
异步提交提供的行为与配置[fsync](http://www.postgres.cn/docs/9.6/runtime-config-wal.html#GUC-FSYNC) = off不同。`fsync`是一个服务器范围的设置,它将会影响所有事务的行为。它禁用了PostgreSQL中所有尝试同步写入到数据库不同部分的逻辑,并且因此一次系统崩溃(即,一个硬件或操作系统崩溃,不是PostgreSQL本身的失败)可能造成数据库状态的任意损坏。在很多情境中,带来大部分性能提升的异步提交可以通过关闭`fsync`来获得,而且不会带来数据损坏的风险。
|
|
||||||
|
|
||||||
[commit_delay](http://www.postgres.cn/docs/9.6/runtime-config-wal.html#GUC-COMMIT-DELAY)也看起来很像异步提交,但它实际上是一种同步提交方法(事实上,`commit_delay`在异步提交时被忽略)。`commit_delay`会使事务在刷写WAL到磁盘之前有一个延迟,它期望由一个这样的事务所执行的刷写能够也服务于其他同时提交的事务。该设置可以被看成是一种时间窗口,在其期间事务可以参与到一次单一的刷写中,这种方式用于在多个事务之间摊销刷写的开销。
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 0x3 查看WAL状态
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 0x5 流复制
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 0x6 Checkpoint
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 相关命令
|
|
||||||
|
|
||||||
- `CHECKPOINT` : 强制一个事务日志检查点
|
|
||||||
|
|
||||||
一个检查点是事务日志序列中的一个点,在该点上所有数据文件 都已经被更新为反映日志中的信息。所有数据文件将被刷写到磁盘。 检查点期间发生的细节可见[第 30.4 节](http://www.postgres.cn/docs/9.6/wal-configuration.html)。
|
|
||||||
|
|
||||||
`CHECKPOINT`命令在发出时强制一个 立即的检查点,而不用等待由系统规划的常规检查点(由 [第 19.5.2 节](http://www.postgres.cn/docs/9.6/runtime-config-wal.html#RUNTIME-CONFIG-WAL-CHECKPOINTS)中的设置控制)。 `CHECKPOINT`不是用来在普通操作中 使用的命令。
|
|
||||||
|
|
||||||
如果在恢复期间执行,`CHECKPOINT` 命令将强制一个重启点(见[第 30.4 节](http://www.postgres.cn/docs/9.6/wal-configuration.html)) 而不是写一个新检查点。
|
|
||||||
|
|
||||||
只有超级用户能够调用`CHECKPOINT`。
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## WAL相关视图与函数
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Checkpoint相关参数
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,306 +0,0 @@
|
|||||||
---
|
|
||||||
author: "Vonng"
|
|
||||||
description: "PostgreSQL中锁的类型,加锁的方法"
|
|
||||||
categories: ["Dev"]
|
|
||||||
tags: ["PostgreSQL","SQL", "Lock"]
|
|
||||||
type: "post"
|
|
||||||
---
|
|
||||||
|
|
||||||
# PostgreSQL的锁
|
|
||||||
|
|
||||||
## 显式加锁
|
|
||||||
|
|
||||||
通常锁会在相应命令执行中自动获取,但也可以手动加锁。
|
|
||||||
|
|
||||||
使用LOCK命令加锁
|
|
||||||
|
|
||||||
```sql
|
|
||||||
LOCK [ TABLE ] [ ONLY ] name [ * ] [, ...] [ IN lockmode MODE ] [ NOWAIT ]
|
|
||||||
```
|
|
||||||
|
|
||||||
### 描述
|
|
||||||
|
|
||||||
* 显式锁表必须在一个事务中进行,在事务外锁表会报错。
|
|
||||||
* `LOCK TABLE`只能获取表锁,默认会等待冲突的锁被释放。
|
|
||||||
* 命令一旦获取到锁, 会被在当前事务中一直持有。没有`UNLOCK TABLE`,锁总是在事务结束时被释放。
|
|
||||||
* 指定`NOWAIT`选项时,如果命令不能立刻获得锁就会中止并报错。
|
|
||||||
* 指定`ONLY`选项,则继承于该表的子表不会自动加锁。
|
|
||||||
|
|
||||||
### 实践
|
|
||||||
|
|
||||||
例如,在迁移数据时,希望在dump和restore的期间,禁止对表的写入,此时可以使用Exclusive显式锁表。
|
|
||||||
|
|
||||||
```sql
|
|
||||||
BEGIN;
|
|
||||||
LOCK TABLE messages IN EXCLUSIVE MODE;
|
|
||||||
-- DO Something
|
|
||||||
COMMIT
|
|
||||||
```
|
|
||||||
|
|
||||||
如果不是使用`sql`而是使用`pg_dump`时,可以采用曲线救国的方式,开启后台进程来锁表。如下例所示:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
function lock_message(){
|
|
||||||
declare -i partition_idx=$1
|
|
||||||
pgurl=$(get_src_url)
|
|
||||||
pipe=/tmp/mpl${partition_idx}
|
|
||||||
catpid=/tmp/mplpid${partition_idx}
|
|
||||||
|
|
||||||
mkfifo ${pipe}
|
|
||||||
cat > ${pipe} &
|
|
||||||
echo $! > ${catpid}
|
|
||||||
cat ${pipe} | psql ${pgurl} &
|
|
||||||
lock_statement="BEGIN;LOCK TABLE rel_8192_${partition_idx}.messages IN EXCLUSIVE MODE;"
|
|
||||||
echo $lock_statement > ${pipe}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function unlock_message(){
|
|
||||||
declare -i partition_idx=$1
|
|
||||||
pipe=/tmp/mpl${partition_idx}
|
|
||||||
catpid=/tmp/mplpid${partition_idx}
|
|
||||||
|
|
||||||
echo "ROLLBACK;\q" > ${pipe}
|
|
||||||
cat ${catpid} | xargs kill
|
|
||||||
rm -rf ${pipe} ${catpid}
|
|
||||||
ps aux | grep "cat\ /tmp*" | grep -v "grep" | awk '{print $2}' | xargs kill
|
|
||||||
echo "UNLOCK rel_8192_${partition_idx}.message"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function handle_message(){
|
|
||||||
lock_message ${partition_idx}
|
|
||||||
psql ${dst_url} -c "TRUNCATE messages;"
|
|
||||||
pg_dump ${src_url} -a -t messages | psql ${dst_url}
|
|
||||||
unlock_message ${partition_idx}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
在为引用表的命令自动获取锁时, PostgreSQL总是尽可能使用最不严格的 锁模式。提供`LOCK TABLE`是用于想要更严格 的锁定的情况。例如,假设一个应用运行一个`READ COMMITTED` 隔离级别的事务, 并且需要确保一个表中的数据在该事务的期间保持稳定。要实现这个目的, 必须在查询之前在表上获得`SHARE`锁模式。这将阻止并发的 数据更改并且确保该表的后续读操作会看到已提交数据的一个稳定视图, 因为`SHARE`锁模式与写入者所要求的 `ROW EXCLUSIVE`锁有冲突,并且你的 `LOCK TABLE *name* IN SHARE MODE` 语句将等待,直到任何并发持有`ROW EXCLUSIVE`模式锁的持有者提交或者回滚。因此,一旦得到锁, 就不会有未提交的写入还没有解决。更进一步,在释放该锁之前,任何 人都不能开始。
|
|
||||||
|
|
||||||
要在运行在`REPEATABLE READ`或`SERIALIZABLE` 隔离级别的事务中得到类似的效果,你必须在执行任何 `SELECT`或者数据修改语句之前执行 `LOCK TABLE`语句。一个 `REPEATABLE READ`或者`SERIALIZABLE`事务的 数据视图将在它的第一个`SELECT`或者数据修改语句开始 时被冻结。在该事务中稍后的一个`LOCK TABLE`仍将阻止并发写 — 但它不会确保该事务读到的东西对应于最新的已提交值。
|
|
||||||
|
|
||||||
如果一个此类事务正要修改表中的数据,那么它应该使用 `SHARE ROW EXCLUSIVE`锁模式来取代 `SHARE`模式。这会保证一次只有一个此类事务运行。如果 不用这种模式,死锁就可能出现:两个事务可能都要求 `SHARE`模式,并且都不能获得 `ROW EXCLUSIVE`模式来真正地执行它们的更新(注意一个 事务所拥有的锁不会冲突,因此一个事务可以在它持有`SHARE` 模式时获得`ROW EXCLUSIVE`模式 — 但是如果有其他 人持有`SHARE`模式时则不能)。为了避免死锁,确保所有的 事务在同样的对象上以相同的顺序获得锁,并且如果在一个对象上涉及多 种锁模式,事务应该总是首先获得最严格的那种模式。
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 参数
|
|
||||||
|
|
||||||
- `*name*`
|
|
||||||
|
|
||||||
要锁定的一个现有表的名称(可以是模式限定的)。如果在表名前指定了 `ONLY`,只有该表会被锁定。如果没有指定了 `ONLY`,该表和它所有的后代表(如果有)都会被锁定。可选 地,在表名后指定`*`来显式地表示把后代表包括在内。命令`LOCK TABLE a, b;`等效于 `LOCK TABLE a; LOCK TABLE b;`。这些表会被按照在 `LOCK TABLE`中指定的顺序一个一个 被锁定。
|
|
||||||
|
|
||||||
- `*lockmode*`
|
|
||||||
|
|
||||||
锁模式指定这个锁和哪些锁冲突。锁模式在 [第 13.3 节](http://www.postgres.cn/docs/9.6/explicit-locking.html)中描述。如果没有指定锁模式,那儿将使用最严格的模式`ACCESS EXCLUSIVE`。
|
|
||||||
|
|
||||||
- `NOWAIT`
|
|
||||||
|
|
||||||
指定`LOCK TABLE`不等待任何冲突锁被释放: 如果所指定的锁不能立即获得,那么事务就会中止。
|
|
||||||
|
|
||||||
## 注解
|
|
||||||
|
|
||||||
`LOCK TABLE ... IN ACCESS SHARE MODE`要求目标表上的 `SELECT`特权。`LOCK TABLE ... IN ROW EXCLUSIVE MODE`要求目标表上的`INSERT`、`UPDATE`、 `DELETE`或`TRUNCATE`特权。所有其他形式的 `LOCK`要求表级`UPDATE`、`DELETE`或`TRUNCATE`特权。
|
|
||||||
|
|
||||||
`LOCK TABLE`在一个事务块外部没有用处:锁将只保持到语句 完成。因此如果在一个事务块外部使用了`LOCK`, PostgreSQL会报告一个错误。使用 [BEGIN](http://www.postgres.cn/docs/9.6/sql-begin.html)和[COMMIT](http://www.postgres.cn/docs/9.6/sql-commit.html)(或者 [ROLLBACK](http://www.postgres.cn/docs/9.6/sql-rollback.html))定义一个事务块。
|
|
||||||
|
|
||||||
`LOCK TABLE`只处理表级锁,因此涉及到 `ROW`的模式名称在这里都是不当的。这些模式名称应该通常 被解读为用户在被锁定表中获取行级锁的意向。还有, `ROW EXCLUSIVE`模式是一个可共享的表锁。记住就 `LOCK TABLE`而言,所有的锁模式都具有相同的语义, 只有模式的冲突规则有所不同。关于如何获取一个真正的行级锁的信息, 请见`SELECT`参考文档中的 [第 13.3.2 节](http://www.postgres.cn/docs/9.6/explicit-locking.html#LOCKING-ROWS)和[*锁定子句*](http://www.postgres.cn/docs/9.6/sql-select.html#SQL-FOR-UPDATE-SHARE)。
|
|
||||||
|
|
||||||
## 示例
|
|
||||||
|
|
||||||
在将要向一个外键表中执行插入时在主键表上获得一个 `SHARE`锁:
|
|
||||||
|
|
||||||
```
|
|
||||||
BEGIN WORK;
|
|
||||||
LOCK TABLE films IN SHARE MODE;
|
|
||||||
SELECT id FROM films
|
|
||||||
WHERE name = 'Star Wars: Episode I - The Phantom Menace';
|
|
||||||
-- 如果记录没有被返回就做 ROLLBACK
|
|
||||||
INSERT INTO films_user_comments VALUES
|
|
||||||
(_id_, 'GREAT! I was waiting for it for so long!');
|
|
||||||
COMMIT WORK;
|
|
||||||
```
|
|
||||||
|
|
||||||
在将要执行一次删除操作前在主键表上取一个 `SHARE ROW EXCLUSIVE`锁:
|
|
||||||
|
|
||||||
```
|
|
||||||
BEGIN WORK;
|
|
||||||
LOCK TABLE films IN SHARE ROW EXCLUSIVE MODE;
|
|
||||||
DELETE FROM films_user_comments WHERE id IN
|
|
||||||
(SELECT id FROM films WHERE rating < 5);
|
|
||||||
DELETE FROM films WHERE rating < 5;
|
|
||||||
COMMIT WORK;
|
|
||||||
```
|
|
||||||
|
|
||||||
## 兼容性
|
|
||||||
|
|
||||||
在 SQL 标准中没有`LOCK TABLE`,SQL 标准中使用 `SET TRANSACTION`指定事务上的并发层次。 PostgreSQL也支持这样做,详见 [SET TRANSACTION](http://www.postgres.cn/docs/9.6/sql-set-transaction.html)。
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 锁的种类:表、行、页
|
|
||||||
|
|
||||||
PostgreSQL提供了多种锁模式用于控制对表中数据的并发访问。 这些模式可以用于在MVCC无法给出期望行为的情境中由应用控制的锁。 同样,大多数PostgreSQL命令会自动要求恰当的锁以保证被引用的表在命令的执行过程中 不会以一种不兼容的方式删除或修改(例如,`TRUNCATE`无法安全地与同一表中上的其他操作并发地执行,因此它在表上获得一个排他锁来强制这种行为)。
|
|
||||||
|
|
||||||
要检查在一个数据库服务器中当前未解除的锁列表,可以使用[`pg_locks`](http://www.postgres.cn/docs/9.6/view-pg-locks.html)系统视图。 有关监控锁管理器子系统状态的更多信息,请参考[第 28 章](http://www.postgres.cn/docs/9.6/monitoring.html)。
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 表锁
|
|
||||||
|
|
||||||
下面的列表显示了可用的锁模式和PostgreSQL自动使用它们的场合。 你也可以用[LOCK](http://www.postgres.cn/docs/9.6/sql-lock.html)命令显式获得这些锁。请记住所有这些锁模式都是表级锁,即使它们的名字包含"row"单词(这些名称是历史遗产)。 在一定程度上,这些名字反应了每种锁模式的典型用法 — 但是语意却都是一样的。 两种锁模式之间真正的区别是它们有着不同的冲突锁模式集合(参考[表 13-2](http://www.postgres.cn/docs/9.6/explicit-locking.html#TABLE-LOCK-COMPATIBILITY))。 两个事务在同一时刻不能在同一个表上持有属于相互冲突模式的锁(但是,一个事务决不会和自身冲突。例如,它可以在同一个表上获得`ACCESS EXCLUSIVE`锁然后接着获取`ACCESS SHARE`锁)。非冲突锁模式可以由许多事务同时持有。 请特别注意有些锁模式是自冲突的(例如,在一个时刻`ACCESS EXCLUSIVE`锁不能被多于一个事务持有)而其他锁模式不是自冲突的(例如,`ACCESS SHARE`锁可以被多个事务持有)。
|
|
||||||
|
|
||||||
**表级锁模式**
|
|
||||||
|
|
||||||
- `ACCESS SHARE`
|
|
||||||
|
|
||||||
只与`ACCESS EXCLUSIVE`锁模式冲突。`SELECT`命令在被引用的表上获得一个这种模式的锁。通常,任何只*读取*表而不修改它的查询都将获得这种锁模式。
|
|
||||||
|
|
||||||
- `ROW SHARE`
|
|
||||||
|
|
||||||
与`EXCLUSIVE`和`ACCESS EXCLUSIVE`锁模式冲突。`SELECT FOR UPDATE`和`SELECT FOR SHARE`命令在目标表上取得一个这种模式的锁 (加上在被引用但没有选择`FOR UPDATE/FOR SHARE`的任何其他表上的`ACCESS SHARE`锁)。
|
|
||||||
|
|
||||||
- `ROW EXCLUSIVE`
|
|
||||||
|
|
||||||
与`SHARE`、`SHARE ROW EXCLUSIVE`、`EXCLUSIVE`和`ACCESS EXCLUSIVE`锁模式冲突。命令`UPDATE`、`DELETE`和`INSERT`在目标表上取得这种锁模式(加上在任何其他被引用表上的`ACCESS SHARE`锁)。通常,这种锁模式将被任何*修改表中数据*的命令取得。
|
|
||||||
|
|
||||||
- `SHARE UPDATE EXCLUSIVE`
|
|
||||||
|
|
||||||
与`SHARE UPDATE EXCLUSIVE`、`SHARE`、`SHARE ROW EXCLUSIVE`、`EXCLUSIVE`和`ACCESS EXCLUSIVE`锁模式冲突。这种模式保护一个表不受并发模式改变和`VACUUM`运行的影响。由`VACUUM`(不带`FULL`)、`ANALYZE`、`CREATE INDEX CONCURRENTLY`和`ALTER TABLE VALIDATE`以及其他`ALTER TABLE`的变体获得。
|
|
||||||
|
|
||||||
- `SHARE`
|
|
||||||
|
|
||||||
与`ROW EXCLUSIVE`、`SHARE UPDATE EXCLUSIVE`、`SHARE ROW EXCLUSIVE`、`EXCLUSIVE`和`ACCESS EXCLUSIVE`锁模式冲突。这种模式保护一个表不受并发数据改变的影响。由`CREATE INDEX`(不带`CONCURRENTLY`)取得。
|
|
||||||
|
|
||||||
- `SHARE ROW EXCLUSIVE`
|
|
||||||
|
|
||||||
与`ROW EXCLUSIVE`、`SHARE UPDATE EXCLUSIVE`、`SHARE`、`SHARE ROW EXCLUSIVE`、`EXCLUSIVE`和`ACCESS EXCLUSIVE`锁模式冲突。这种模式保护一个表不受并发数据修改所影响,并且是自排他的,这样在一个时刻只能有一个会话持有它。由`CREATE TRIGGER`和很多 `ALTER TABLE`的很多形式所获得(见 [ALTER TABLE](http://www.postgres.cn/docs/9.6/sql-altertable.html))。
|
|
||||||
|
|
||||||
- `EXCLUSIVE`
|
|
||||||
|
|
||||||
与`ROW SHARE`、`ROW EXCLUSIVE`、`SHARE UPDATE EXCLUSIVE`、`SHARE`、`SHARE ROW EXCLUSIVE`、`EXCLUSIVE`和`ACCESS EXCLUSIVE`锁模式冲突。这种模式只允许并发的`ACCESS SHARE`锁,即只有来自于表的读操作可以与一个持有该锁模式的事务并行处理。由`REFRESH MATERIALIZED VIEW CONCURRENTLY`获得。
|
|
||||||
|
|
||||||
- `ACCESS EXCLUSIVE`
|
|
||||||
|
|
||||||
与所有模式的锁冲突(`ACCESS SHARE`、`ROW SHARE`、`ROW EXCLUSIVE`、`SHARE UPDATE EXCLUSIVE`、`SHARE`、`SHARE ROW EXCLUSIVE`、`EXCLUSIVE`和`ACCESS EXCLUSIVE`)。这种模式保证持有者是访问该表的唯一事务。由`ALTER TABLE`、`DROP TABLE`、`TRUNCATE`、`REINDEX`、`CLUSTER`、`VACUUM FULL`和`REFRESH MATERIALIZED VIEW`(不带`CONCURRENTLY`)命令获取。`ALTER TABLE`的很多形式也在这个层面上获得锁(见[ALTER TABLE](http://www.postgres.cn/docs/9.6/sql-altertable.html))。这也是未显式指定模式的`LOCK TABLE`命令的默认锁模式。
|
|
||||||
|
|
||||||
> **提示: **只有一个`ACCESS EXCLUSIVE`锁阻塞一个`SELECT`(不带`FOR UPDATE/SHARE`)语句。
|
|
||||||
|
|
||||||
一旦被获取,一个锁通常将被持有直到事务结束。 但是如果在建立保存点之后才获得锁,那么在回滚到这个保存点的时候将立即释放该锁。 这与`ROLLBACK`取消保存点之后所有的影响的原则保持一致。 同样的原则也适用于在PL/pgSQL异常块中获得的锁:一个跳出块的错误将释放在块中获得的锁。
|
|
||||||
|
|
||||||
**表 13-2. 冲突的锁模式**
|
|
||||||
|
|
||||||
| 请求的锁模式 | 当前的锁模式 | | | | | | | |
|
|
||||||
| ---------------------- | --------- | ------------- | ---------------------- | ----- | ------------------- | --------- | ---------------- | ---- |
|
|
||||||
| ACCESS SHARE | ROW SHARE | ROW EXCLUSIVE | SHARE UPDATE EXCLUSIVE | SHARE | SHARE ROW EXCLUSIVE | EXCLUSIVE | ACCESS EXCLUSIVE | |
|
|
||||||
| ACCESS SHARE | | | | | | | | X |
|
|
||||||
| ROW SHARE | | | | | | | X | X |
|
|
||||||
| ROW EXCLUSIVE | | | | | X | X | X | X |
|
|
||||||
| SHARE UPDATE EXCLUSIVE | | | | X | X | X | X | X |
|
|
||||||
| SHARE | | | X | X | | X | X | X |
|
|
||||||
| SHARE ROW EXCLUSIVE | | | X | X | X | X | X | X |
|
|
||||||
| EXCLUSIVE | | X | X | X | X | X | X | X |
|
|
||||||
| ACCESS EXCLUSIVE | X | X | X | X | X | X | X | X |
|
|
||||||
|
|
||||||
### 行锁
|
|
||||||
|
|
||||||
除了表级锁以外,还有行级锁,在下文列出了行级锁以及在哪些情境下PostgreSQL会自动使用它们。行级锁的完整冲突表请见[表 13-3](http://www.postgres.cn/docs/9.6/explicit-locking.html#ROW-LOCK-COMPATIBILITY)。注意一个事务可能会在相同的行上保持冲突的锁,甚至是在不同的子事务中。但是除此之外,两个事务永远不可能在相同的行上持有冲突的锁。行级锁不影响数据查询,它们只阻塞对同一行的*写入者和加锁者*。
|
|
||||||
|
|
||||||
**行级锁模式**
|
|
||||||
|
|
||||||
- `FOR UPDATE`
|
|
||||||
|
|
||||||
`FOR UPDATE`会导致由`SELECT`语句检索到的行被锁定,就好像它们要被更新。这可以阻止它们被其他事务锁定、修改或者删除,一直到当前事务结束。也就是说其他尝试`UPDATE`、`DELETE`、`SELECT FOR UPDATE`、`SELECT FOR NO KEY UPDATE`、`SELECT FOR SHARE`或者`SELECT FOR KEY SHARE`这些行的事务将被阻塞,直到当前事务结束。反过来,`SELECT FOR UPDATE`将等待已经在相同行上运行以上这些命令的并发事务,并且接着锁定并且返回被更新的行(或者没有行,因为行可能已被删除)。不过,在一个`REPEATABLE READ`或`SERIALIZABLE`事务中,如果一个要被锁定的行在事务开始后被更改,将会抛出一个错误。进一步的讨论请见[第 13.4 节](http://www.postgres.cn/docs/9.6/applevel-consistency.html)。任何在一行上的`DELETE`命令也会获得`FOR UPDATE`锁模式,在某些列上修改值的`UPDATE`也会获得该锁模式。当前`UPDATE`情况中被考虑的列集合是那些具有能用于外键的唯一索引的列(所以部分索引和表达式索引不被考虑),但是这种要求未来有可能会改变。
|
|
||||||
|
|
||||||
- `FOR NO KEY UPDATE`
|
|
||||||
|
|
||||||
行为与`FOR UPDATE`类似,不过获得的锁较弱:这种锁将不会阻塞尝试在相同行上获得锁的`SELECT FOR KEY SHARE`命令。任何不获取`FOR UPDATE`锁的`UPDATE`也会获得这种锁模式。
|
|
||||||
|
|
||||||
- `FOR SHARE`
|
|
||||||
|
|
||||||
行为与`FOR NO KEY UPDATE`类似,不过它在每个检索到的行上获得一个共享锁而不是排他锁。一个共享锁会阻塞其他事务在这些行上执行`UPDATE`、`DELETE`、`SELECT FOR UPDATE`或者`SELECT FOR NO KEY UPDATE`,但是它不会阻止它们执行`SELECT FOR SHARE`或者`SELECT FOR KEY SHARE`。
|
|
||||||
|
|
||||||
- `FOR KEY SHARE`
|
|
||||||
|
|
||||||
行为与`FOR SHARE`类似,不过锁较弱:`SELECT FOR UPDATE`会被阻塞,但是`SELECT FOR NO KEY UPDATE`不会被阻塞。一个键共享锁会阻塞其他事务执行修改键值的`DELETE`或者`UPDATE`,但不会阻塞其他`UPDATE`,也不会阻止`SELECT FOR NO KEY UPDATE`、`SELECT FOR SHARE`或者`SELECT FOR KEY SHARE`。
|
|
||||||
|
|
||||||
PostgreSQL不会在内存里保存任何关于已修改行的信息,因此对一次锁定的行数没有限制。 不过,锁住一行会导致一次磁盘写,例如, `SELECT FOR UPDATE`将修改选中的行以标记它们被锁住,并且因此会导致磁盘写入。
|
|
||||||
|
|
||||||
**表 13-3. 冲突的行级锁**
|
|
||||||
|
|
||||||
| 要求的锁模式 | 当前的锁模式 | | | |
|
|
||||||
| ----------------- | --------- | ----------------- | ---------- | ---- |
|
|
||||||
| FOR KEY SHARE | FOR SHARE | FOR NO KEY UPDATE | FOR UPDATE | |
|
|
||||||
| FOR KEY SHARE | | | | X |
|
|
||||||
| FOR SHARE | | | X | X |
|
|
||||||
| FOR NO KEY UPDATE | | X | X | X |
|
|
||||||
| FOR UPDATE | X | X | X | X |
|
|
||||||
|
|
||||||
### 页锁
|
|
||||||
|
|
||||||
除了表级别和行级别的锁以外,页面级别的共享/排他锁被用来控制对共享缓冲池中表页面的读/写。 这些锁在行被抓取或者更新后马上被释放。应用开发者通常不需要关心页级锁,我们在这里提到它们只是为了完整。
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 死锁
|
|
||||||
|
|
||||||
显式锁定的使用可能会增加*死锁*的可能性,死锁是指两个(或多个)事务相互持有对方想要的锁。例如,如果事务 1 在表 A 上获得一个排他锁,同时试图获取一个在表 B 上的排他锁, 而事务 2 已经持有表 B 的排他锁,同时却正在请求表 A 上的一个排他锁,那么两个事务就都不能进行下去。PostgreSQL能够自动检测到死锁情况并且会通过中断其中一个事务从而允许其它事务完成来解决这个问题(具体哪个事务会被中断是很难预测的,而且也不应该依靠这样的预测)。
|
|
||||||
|
|
||||||
要注意死锁也可能会作为行级锁的结果而发生(并且因此,它们即使在没有使用显式锁定的情况下也会发生)。考虑如下情况,两个并发事务在修改一个表。第一个事务执行:
|
|
||||||
|
|
||||||
```
|
|
||||||
UPDATE accounts SET balance = balance + 100.00 WHERE acctnum = 11111;
|
|
||||||
```
|
|
||||||
|
|
||||||
这样就在指定帐号的行上获得了一个行级锁。然后,第二个事务执行:
|
|
||||||
|
|
||||||
```
|
|
||||||
UPDATE accounts SET balance = balance + 100.00 WHERE acctnum = 22222;
|
|
||||||
UPDATE accounts SET balance = balance - 100.00 WHERE acctnum = 11111;
|
|
||||||
```
|
|
||||||
|
|
||||||
第一个`UPDATE`语句成功地在指定行上获得了一个行级锁,因此它成功更新了该行。 但是第二个`UPDATE`语句发现它试图更新的行已经被锁住了,因此它等待持有该锁的事务结束。事务二现在就在等待事务一结束,然后再继续执行。现在,事务一执行:
|
|
||||||
|
|
||||||
```
|
|
||||||
UPDATE accounts SET balance = balance - 100.00 WHERE acctnum = 22222;
|
|
||||||
```
|
|
||||||
|
|
||||||
事务一试图在指定行上获得一个行级锁,但是它得不到:事务二已经持有了这样的锁。所以它要等待事务二完成。因此,事务一被事务二阻塞,而事务二也被事务一阻塞:一个死锁。 PostgreSQL将检测这样的情况并中断其中一个事务。
|
|
||||||
|
|
||||||
防止死锁的最好方法通常是保证所有使用一个数据库的应用都以一致的顺序在多个对象上获得锁。在上面的例子里,如果两个事务以同样的顺序更新那些行,那么就不会发生死锁。 我们也应该保证一个事务中在一个对象上获得的第一个锁是该对象需要的最严格的锁模式。如果我们无法提前验证这些,那么可以通过重试因死锁而中断的事务来即时处理死锁。
|
|
||||||
|
|
||||||
只要没有检测到死锁情况,寻求一个表级或行级锁的事务将无限等待冲突锁被释放。这意味着一个应用长时间保持事务开启不是什么好事(例如等待用户输入)。
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 咨询锁
|
|
||||||
|
|
||||||
PostgreSQL提供了一种方法创建由应用定义其含义的锁。这种锁被称为*咨询锁*,因为系统并不强迫其使用 — 而是由应用来保证其正确的使用。咨询锁可用于 MVCC 模型不适用的锁定策略。例如,咨询锁的一种常用用法是模拟所谓"平面文件"数据管理系统典型的悲观锁策略。虽然一个存储在表中的标志可以被用于相同目的,但咨询锁更快、可以避免表膨胀并且会由服务器在会话结束时自动清理。
|
|
||||||
|
|
||||||
有两种方法在PostgreSQL中获取一个咨询锁:在会话级别或在事务级别。一旦在会话级别获得了咨询锁,它将被保持直到被显式释放或会话结束。不同于标准锁请求,会话级咨询锁请求不尊重事务语义:在一个后来被回滚的事务中得到的锁在回滚后仍然被保持,并且同样即使调用它的事务后来失败一个解锁也是有效的。一个锁在它所属的进程中可以被获取多次;对于每一个完成的锁请求必须有一个相应的解锁请求,直至锁被真正释放。在另一方面,事务级锁请求的行为更像普通锁请求:在事务结束时会自动释放它们,并且没有显式的解锁操作。这种行为通常比会话级别的行为更方便,因为它使用一个咨询锁的时间更短。对于同一咨询锁标识符的会话级别和事务级别的锁请求按照期望将彼此阻塞。如果一个会话已经持有了一个给定的咨询锁,由它发出的附加请求将总是成功,即使有其他会话在等待该锁;不管现有的锁和新请求是处在会话级别还是事务级别,这种说法都是真的。
|
|
||||||
|
|
||||||
和所有PostgreSQL中的锁一样,当前被任何会话所持有的咨询锁的完整列表可以在[`pg_locks`](http://www.postgres.cn/docs/9.6/view-pg-locks.html)系统视图中找到。
|
|
||||||
|
|
||||||
咨询锁和普通锁都被存储在一个共享内存池中,它的尺寸由[max_locks_per_transaction](http://www.postgres.cn/docs/9.6/runtime-config-locks.html#GUC-MAX-LOCKS-PER-TRANSACTION)和[max_connections](http://www.postgres.cn/docs/9.6/runtime-config-connection.html#GUC-MAX-CONNECTIONS)配置变量定义。 必须当心不要耗尽这些内存,否则服务器将不能再授予任何锁。这对服务器可以授予的咨询锁数量设置了一个上限,根据服务器的配置不同,这个限制通常是数万到数十万。
|
|
||||||
|
|
||||||
在使用咨询锁方法的特定情况下,特别是查询中涉及显式排序和`LIMIT`子句时,由于 SQL 表达式被计算的顺序,必须小心控制锁的获取。例如:
|
|
||||||
|
|
||||||
```
|
|
||||||
SELECT pg_advisory_lock(id) FROM foo WHERE id = 12345; -- ok
|
|
||||||
SELECT pg_advisory_lock(id) FROM foo WHERE id > 12345 LIMIT 100; -- danger!
|
|
||||||
SELECT pg_advisory_lock(q.id) FROM
|
|
||||||
(
|
|
||||||
SELECT id FROM foo WHERE id > 12345 LIMIT 100
|
|
||||||
) q; -- ok
|
|
||||||
```
|
|
||||||
|
|
||||||
在上述查询中,第二种形式是危险的,因为不能保证在锁定函数被执行之前应用`LIMIT`。这可能导致获得某些应用不期望的锁,并因此在会话结束之前无法释放。 从应用的角度来看,这样的锁将被挂起,虽然它们仍然在`pg_locks`中可见。
|
|
||||||
|
|
||||||
提供的操作咨询锁函数在[第 9.26.10 节](http://www.postgres.cn/docs/9.6/functions-admin.html#FUNCTIONS-ADVISORY-LOCKS)中描述。
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,141 +0,0 @@
|
|||||||
# Autovacuum Tuning Basics [译]
|
|
||||||
|
|
||||||
原文:https://blog.2ndquadrant.com/autovacuum-tuning-basics/#PostgreSQL%20Performance%20Tuning
|
|
||||||
|
|
||||||
几个星期前,我介绍了调优检查点的基础知识,在那篇文章中,我还提到了性能问题的第二个常见原因是autovacuum(根据我们在邮件列表和我们的客户支持下看到的)。所以让我跟随这个帖子关于自动调谐的基础知识。我将非常简要地解释必要的理论(死元组,膨胀和autovacuum如何处理它),但是这篇博文的主要重点是调优 - 那里有什么配置选项,经验法则等等。
|
|
||||||
|
|
||||||
### 死元组
|
|
||||||
|
|
||||||
首先,让我们简单地解释一下“死亡元组”和“膨胀”(如果你想得到更详细的解释,可以阅读Joe Nelson的文章,详细讨论这个)。
|
|
||||||
|
|
||||||
当您在PostgreSQL中执行DELETE时,行(又名元组)不会立即从数据文件中删除。相反,它只能通过在行首部中设置xmax字段来标记为已删除。对于UPDATE,也可以看作PostgreSQL中的DELETE + INSERT。
|
|
||||||
|
|
||||||
这是PostgreSQL MVCC背后的基本思想之一,因为它允许更大的并发性,只需在不同的进程之间进行最小程度的锁定。这个MVCC实现的缺点当然是它留下了被删除的元组,甚至在所有可能看到这些版本的事务完成之后。
|
|
||||||
|
|
||||||
如果不清理,这些“死元组”(对于任何事务实际上是不可见的)将永远停留在数据文件中,浪费磁盘空间,而对于有许多DELETE和UPDATE的表,死元组可能会占用绝大部分磁盘空间。当然,这些死元组也会被索引引用,进一步增加了浪费的磁盘空间。这就是我们在PostgreSQL中所谓的“膨胀”。当然,查询必须处理的数据越多(即使99%的数据立即被“抛弃”),查询就越慢。
|
|
||||||
|
|
||||||
### Vacuum和AutoVacuum
|
|
||||||
|
|
||||||
回收死元组占用的空间的最直接的方法是(通过手动运行VACUUM命令)。这个维护命令将扫描表并从表和索引中删除死元组 - 它通常不会将磁盘空间返回给操作系统,但会使其可用于新行。
|
|
||||||
|
|
||||||
注意:`VACUUM FULL`会回收空间并将其返回给操作系统,但是有一些缺点。首先它获得对表的**独占锁(Access Exclusive)**,阻止所有操作(包括SELECT)。其次,它本质上创建了表的副本,使所需的磁盘空间增加了一倍,所以当磁盘空间已经不足时不太实际。
|
|
||||||
|
|
||||||
`VACUUM`的麻烦在于它完全是手动操作 - 只有在您决定运行时才会发生,而不是在需要时才会发生。你可以把它放到cron中,每5分钟在所有的表上运行一次,但是大部分运行的机会并不会真正清理任何东西,唯一的影响是CPU和I / O在系统上的使用率会更高。或者你可能每天晚上只运行一次,在这种情况下,你可能会累积更多你想要的死元组。
|
|
||||||
|
|
||||||
这是我们使用**自动清理(AutoVacuum)**的主要目的;根据需要进行清理,以控制浪费空间的数量。数据库确实知道随着时间的推移生成了多少个死元组(每个事务都报告它删除和更新的元组的数量),当表累积了一定数量的死元组时,可以触发清理工作(默认情况下是20%表,我们将会看到)。所以在繁忙的时候会更频繁地执行,而当数据库大部分空闲的时候则更少。
|
|
||||||
|
|
||||||
### autoanalyze
|
|
||||||
|
|
||||||
清理死元组不是自动清理的唯一任务。它还负责更新优化程序在规划查询时使用的数据分布统计信息。您可以通过运行ANALYZE来手动收集这些数据,但是会遇到与VACUUM类似的问题 - 您可能会经常运行或不经常运行。
|
|
||||||
|
|
||||||
解决方案也是类似的 - 数据库可以观察表中有多少行更改,并自动运行ANALYZE。
|
|
||||||
|
|
||||||
注意:ANALYZE的开销要大一点,因为虽然VACUUM的成本与死元组的数量成正比(开销很小,当死元组数量很少时/没有),ANALYZE必须在每次执行时从头开始重建统计数据。另一方面,如果你不是经常运行它,那么不好的计划选择的代价可能同样严重。
|
|
||||||
|
|
||||||
为了简洁起见,我会在后面的文章中大部分忽略这个自动清理任务 - 配置与清理非常相似,并且遵循大致相同的推理。
|
|
||||||
|
|
||||||
### 监控
|
|
||||||
|
|
||||||
在进行任何调整之前,您需要能够收集相关数据,否则您怎么能说您需要进行任何调整,或评估配置更改的影响?
|
|
||||||
|
|
||||||
换句话说,你应该有一些基本的监控,从数据库中收集指标。为了清理,你至少要看这些值:
|
|
||||||
|
|
||||||
|
|
||||||
* `pg_stat_all_tables.n_dead_tup` :每个表中的死元组数(用户表和系统目录)
|
|
||||||
* `(n_dead_tup / n_live_tup)` : 每个表中死/活元组的比率
|
|
||||||
* `(pg_class.relpages / pg_class.reltuples)`: 每行空格
|
|
||||||
|
|
||||||
|
|
||||||
如果您已经部署了监控系统(应该的),那么您很有可能已经收集了这些指标。总体目标是获得稳定的行为,这些指标没有突然或显着的变化。
|
|
||||||
|
|
||||||
还有一个方便的pgstattuple扩展,允许您对表和索引执行分析,包括计算可用空间量,死元组等。
|
|
||||||
|
|
||||||
### 调整目标
|
|
||||||
|
|
||||||
在查看实际配置参数之前,让我们简要讨论一下高级调优目标,即更改参数时要实现的目标:
|
|
||||||
|
|
||||||
清理死元组 - 保持合理低的磁盘空间量,而不是浪费不合理的磁盘空间量,防止索引膨胀并保持查询速度。
|
|
||||||
最大限度地减少清理的影响 - 不要经常执行清理工作,因为这会浪费资源(CPU,I / O和RAM),并可能会严重影响性能。
|
|
||||||
|
|
||||||
也就是说,你需要找到适当的平衡 - 经常运行可能会不够经常运行。天平在很大程度上取决于您所管理的数据量,您正在处理的工作负载类型(DELETE / UPDATE数量)。
|
|
||||||
|
|
||||||
`postgresql.conf`中的大多数默认值是相当保守的,原因有二。首先,根据当时常见的资源(CPU,RAM,...),默认值是几年前决定的。其次,我们希望默认配置可以在任何地方工作,包括树莓派(Raspberry Pi)或小型VPS服务器等小型机器。对于许多部署(特别是较小的部署和/或处理大部分读取工作负载),默认的配置参数将工作得很好。
|
|
||||||
|
|
||||||
随着数据库大小和/或写入数量的增加,问题开始出现。典型的问题是清理不经常发生,当发生这种情况时会大大地干扰性能,因为它必须处理大量的垃圾。如果这些情况你应该遵循这个简单的规则:
|
|
||||||
|
|
||||||
* 如果它造成了损失,你不会经常这样做。
|
|
||||||
|
|
||||||
|
|
||||||
* 也就是说,调整参数,使得清理更频繁,每次处理更少量的死元组。
|
|
||||||
|
|
||||||
注意:人们有时遵循不同的规则 - 如果伤害了,不要这样做。 - 完全禁用自动清理。除非你真的(真的)知道你在做什么,并且有定期的清理脚本,否则请不要这样做。否则,你正在画一个角落,而不是性能有些下降,你将不得不面对严重退化的表现,甚至可能是停机。
|
|
||||||
|
|
||||||
所以,现在我们知道我们想要通过调谐实现什么,让我们看看配置参数...
|
|
||||||
|
|
||||||
### 阈值和比例因子
|
|
||||||
|
|
||||||
当然,你可能会调整的第一件事情是当清理被触发,这是受两个参数的影响:
|
|
||||||
|
|
||||||
autovacuum_vacuum_threshold = 50
|
|
||||||
autovacuum_vacuum_scale_factor = 0.2
|
|
||||||
并且每当无用元组的数目(你可以看到pg_stat_all_tables.n_dead_tup)超过
|
|
||||||
|
|
||||||
阈值+ pg_class.reltuples * scale_factor
|
|
||||||
|
|
||||||
该表将被视为需要清理。该公式基本上说,高达20%的表可能是清理前的死元组(50行的阈值是为了防止对小表进行非常频繁的清理)。
|
|
||||||
|
|
||||||
默认比例因子适用于中小型的表格,但对于非常大型的表格来说效果并不理想 - 在10GB的表格上,这大概是2GB的死元组,而在1TB的表格上则是200GB。
|
|
||||||
|
|
||||||
这是一个积累了大量死元组的例子,并且一次处理所有的元组,这将会受到伤害。根据之前提到的规则,解决方案是通过显着降低比例因子来更频繁地执行此操作,甚至可能是这样:
|
|
||||||
|
|
||||||
autovacuum_vacuum_scale_factor = 0.01
|
|
||||||
|
|
||||||
这将限制仅减少到表格的1%。另一种解决方法是完全放弃比例因子,仅使用阈值
|
|
||||||
|
|
||||||
autovacuum_vacuum_scale_factor = 0
|
|
||||||
autovacuum_vacuum_threshold = 10000
|
|
||||||
|
|
||||||
这应该在产生10000个死元组之后触发清理。
|
|
||||||
|
|
||||||
一个麻烦的问题是postgresql.conf中的这些变化会影响到所有表(实际上是整个集群),并且可能会对小型表(包括系统目录)的清理产生不利影响。
|
|
||||||
|
|
||||||
当小桌子被更频繁地清理时,最简单的解决办法就是完全忽略这个问题。小桌子的清理将是相当便宜的,并改善工人数量
|
|
||||||
|
|
||||||
还没有提到的一个配置选项是autovacuum_max_workers,那是什么意思?那么,清理不会发生在一个自动清理过程中,但是数据库可以启动到实际清理不同数据库/表的autovacuum_max_workers进程。
|
|
||||||
|
|
||||||
这很有用,因为例如你不想停止清理小表,直到完成一个大表的清理(这可能需要相当多的时间,因为节流)。
|
|
||||||
|
|
||||||
麻烦的是用户假定工作人员的数量与可能发生的清理量成正比。如果将自动清扫工人的数量增加到6人,那么与默认的3人相比,工作量肯定会增加一倍,对吧?
|
|
||||||
|
|
||||||
那么,不。几段前面描述的成本上限是全球性的,所有的汽车真空工人都是共同的。每个工作进程只获得总成本限制的1 / autovacuum_max_workers,所以增加工作者数量只会使他们变慢。
|
|
||||||
|
|
||||||
这有点像高速公路 - 车辆数量增加一倍,但速度减半,只能让你每小时到达目的地的人数相同。
|
|
||||||
|
|
||||||
所以如果你的数据库的清理跟不上用户的活动,那么增加工作者的数量并不是一个解决方案,除非你也调整了其他的参数。
|
|
||||||
|
|
||||||
### 按表限流
|
|
||||||
|
|
||||||
实际上,当我说成本限制是全局的,所有的自动清理Worker共同分担的时候,我一直在说谎。与缩放因子和阈值类似,可以设置每个表的成本限制和延迟:
|
|
||||||
|
|
||||||
```sql
|
|
||||||
ALTER TABLE t SET (autovacuum_vacuum_cost_limit = 1000);
|
|
||||||
ALTER TABLE t SET (autovacuum_vacuum_cost_delay = 10);
|
|
||||||
```
|
|
||||||
|
|
||||||
全球成本计算中不包括处理这些表的工人,而是独立地进行限制。
|
|
||||||
|
|
||||||
这给了你相当多的灵活性和权力,但不要忘记 - 拥有巨大的权力是很大的责任!
|
|
||||||
|
|
||||||
在实践中,我们几乎从不使用这个功能,有两个基本的原因。首先,您通常希望在后台清理上使用单个全局限制。其次,多个工人有时被扼杀在一起,有时独立地使监督和分析系统的行为变得更加困难。
|
|
||||||
|
|
||||||
### 概要
|
|
||||||
|
|
||||||
所以这就是你调整自动清理的方法。如果我必须把它归纳成几条基本规则,那就是这五条:
|
|
||||||
|
|
||||||
* 说真的,不要禁用autovacuum,除非你真的知道你在做什么。
|
|
||||||
* 在繁忙的数据库上(做大量更新和删除),特别是大数据库,你可能应该减小比例因子,这样更频繁地进行清理。
|
|
||||||
* 在合理的硬件(良好的存储,多核心)上,你应该增加节流参数,以便清理能够跟上。
|
|
||||||
* 单独增加autovacuum_max_workers在大多数情况下并不会真的有帮助。你会得到更多的进程变慢。
|
|
||||||
* 你可以使用ALTER TABLE来设置每个表的参数,但是如果你真的需要的话,可以考虑一下。这使得系统更复杂,更难以检查。
|
|
||||||
|
|
||||||
我原本包括几个部分,解释什么时候autovacuum没有真正的工作,以及如何检测它们(以及什么是最好的解决方案),但博客文章已经太长了,所以我会在几天后分开发布。
|
|
@ -1,151 +0,0 @@
|
|||||||
# Checkpoints Tuning Basics [译]
|
|
||||||
|
|
||||||
原文地址:https://blog.2ndquadrant.com/basics-of-tuning-checkpoints/
|
|
||||||
|
|
||||||
在进行不重复的写操作的系统上,调优检查点对于获得良好性能至关重要。然而,检查点是我们经常在社区邮件列表和我们客户的性能调整评估过程中发现混淆和配置问题的领域之一。 (另外一个是autovacuum,前几天由Citus的Joe Nelson讨论过。)那么让我来引导你通过检查点 - 他们做什么以及如何在PostgreSQL中调整它们。
|
|
||||||
|
|
||||||
## 什么是检查点?
|
|
||||||
|
|
||||||
PostgreSQL是依赖于预写日志(WAL)的数据库之一 - 所有的改变都会先写入一个日志(一系列更改),然后再写入数据文件。这提供了持久性,因为在发生崩溃的情况下,数据库可能会使用WAL来执行恢复 - 从WAL读取更改并将其重新应用于数据文件。
|
|
||||||
|
|
||||||
虽然这可能会使写入量增加一倍,但实际上可能会提高性能。用户只需等待WAL(刷新到磁盘),而数据文件只在内存中修改,然后在后台刷新。这很好,因为虽然WAL写入本质上是连续的,但写入数据文件通常是随机的。
|
|
||||||
|
|
||||||
假设系统崩溃,数据库需要执行恢复。最简单的方法是从头开始,从头开始重放整个WAL。最后,我们应该得到一个完整和正确的数据库。缺点当然是需要保留和重放整个WAL。我们经常处理的数据库不是非常大(比如几百GB),但是每天产生几个TB的WAL。所以想象在运行一年的数据库上,需要多少磁盘空间来保持所有的WAL,以及在恢复过程中需要多少时间来重放。
|
|
||||||
|
|
||||||
但是如果数据库保证在给定的WAL位置(wal_lsn)处的所有数据变更都已经落盘。那么就可以在恢复期间确定这个位置,并仅重播WAL的剩余部分,从而显着减少恢复时间。而且它也可以移除“已知的安全点”之前的WAL。
|
|
||||||
|
|
||||||
这正是检查点的目的 - 确保WAL在某个时间点之前不再需要进行恢复,从而减少磁盘空间要求和恢复时间。
|
|
||||||
|
|
||||||
*注意:如果你碰巧是一个玩家,你可能对检查点的概念很熟悉 - 你的角色通过了游戏中的某个点,如果你没有击败下一个老板或掉入一个湖泊热熔岩,你从最后一点,而不是从一开始就开始。让我们看看如何在PostgreSQL中实现这一点;-)*
|
|
||||||
|
|
||||||
还有另外一个极端 :非常频繁的检查点(例如每隔一秒钟)。这样可以只保留微量的WAL,恢复极快(不得不重播只有微小的WAL量)。但它也会从异步写入数据页面退化为同步写入,严重影响用户(例如增加COMMIT延迟,减少吞吐量)。
|
|
||||||
|
|
||||||
所以在实践中通常希望检查点不要频繁到影响用户,但又足够频繁以限制恢复和磁盘空间需求的时间。
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 触发检查点
|
|
||||||
|
|
||||||
有三到四个原因可以触发检查点:
|
|
||||||
|
|
||||||
* 直接执行CHECKPOINT命令
|
|
||||||
* 执行需要检查点的命令(例如,pg_start_backup,CREATE DATABASE或pg_ctl stop | restart等)
|
|
||||||
* 达到自上次检查点以来的配置时间量
|
|
||||||
* 从上一个检查点(又名“用完WAL”或“填充WAL”)生成配置的WAL量
|
|
||||||
|
|
||||||
前两点在这里相当不重要 - 那些是罕见的,手动触发的事件。这篇博文是关于如何配置其他两个事件,影响常规定期检查点。
|
|
||||||
|
|
||||||
这些时间/大小限制是使用两个配置选项设置的:
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
* `checkpoint_timeout = 5min`
|
|
||||||
* `max_wal_size = 1GB`(在PostgreSQL 9.5之前是`checkpoint_segments`)
|
|
||||||
|
|
||||||
使用这些(默认)值,PostgreSQL将每5分钟触发一次CHECKPOINT,或者在WAL增长到磁盘上大约1GB之后。
|
|
||||||
|
|
||||||
注意:max_wal_size是总WAL大小的软限制,这有两个结果。首先,数据库将尽量不超过它,但是被允许,因此在分区上保留足够的空闲空间并监视它。其次,这不是一个“每个检查点”的限制 - 由于扩展检查点(稍后解释),WAL配额被分为2-3个检查点。因此,根据checkpoint_completion_target,使用max_wal_size,数据库将在写入300-500 MB的WAL之后启动CHECKPOINT。
|
|
||||||
|
|
||||||
默认值相当低,就像示例配置文件中的大多数其他默认值一样,即使在像Raspberry Pi这样的小型系统上也可以工作。
|
|
||||||
|
|
||||||
但是,如何确定您的系统的良好价值?我们的目标不是过分频繁地或不经常地检查点,而我们的调整“最佳实践”包括两个步骤:
|
|
||||||
|
|
||||||
* 选择一个“合理”的`checkpoint_timeout`值
|
|
||||||
* 设置`max_wal_size`足够高,很少到达
|
|
||||||
|
|
||||||
很难说checkpoint_timeout的“合理”值是多少,因为它取决于恢复时间目标(RTO),即什么是可接受的最大恢复持续时间。
|
|
||||||
|
|
||||||
但是,这有点棘手,因为checkpoint_timeout是生成WAL需要多长时间的限制,而不是直接在恢复时间上。而遗憾的是,无法确切说明需要多长时间才能恢复。 WAL通常由多个进程(运行DML)生成,而恢复则由单个进程执行(这种限制大多是固有的,不可能很快消失)。这不仅会影响本地恢复,还会影响到流式复制到备用数据库。当然,本地恢复通常在重启后立即发生,当文件系统缓存很冷时。
|
|
||||||
|
|
||||||
但一般来说,默认值(5分钟)是相当低的,30分钟到1小时之间的值是相当普遍的。 PostgreSQL 9.6甚至将最大值增加到1天(所以有些黑客认为这是一个好的主意)。由于整页写入,低值也可能导致写入放大(我不打算在这里讨论)。
|
|
||||||
|
|
||||||
假设我们已经决定使用30分钟。
|
|
||||||
|
|
||||||
```
|
|
||||||
checkpoint_timeout = 30min
|
|
||||||
```
|
|
||||||
|
|
||||||
现在我们需要估计数据库在30分钟内产生多少WAL,以便我们可以使用它来获得max_wal_size。 有几种方法可以确定生成多少WAL:
|
|
||||||
|
|
||||||
* 使用`pg_current_xlog_insert_location()` (10之后是`pg_current_wal_insert_lsn`)查看实际的WAL位置(基本上在文件中偏移),并计算每30分钟测量的位置之间的差异。
|
|
||||||
* 启用`log_checkpoints = on`,然后从服务器日志中提取信息(每个完成的检查点都会有详细的统计信息,包括WAL的数量)。
|
|
||||||
* 使用来自pg_stat_bgwriter的数据,其中也包含关于检查点数量的信息(可以结合当前max_wal_size值的知识)。
|
|
||||||
|
|
||||||
例如,我们使用第一种方法。 在运行pgbench的测试机上,我看到:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
postgres=# SELECT pg_current_xlog_insert_location();
|
|
||||||
pg_current_xlog_insert_location
|
|
||||||
---------------------------------
|
|
||||||
3D/B4020A58
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
... after 5 minutes ...
|
|
||||||
|
|
||||||
postgres=# SELECT pg_current_xlog_insert_location();
|
|
||||||
pg_current_xlog_insert_location
|
|
||||||
---------------------------------
|
|
||||||
3E/2203E0F8
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
postgres=# SELECT pg_xlog_location_diff('3E/2203E0F8', '3D/B4020A58');
|
|
||||||
pg_xlog_location_diff
|
|
||||||
-----------------------
|
|
||||||
1845614240
|
|
||||||
(1 row)
|
|
||||||
```
|
|
||||||
|
|
||||||
这表明在5分钟内,数据库生成了〜1.8GB的WAL,因此`checkpoint_timeout = 30min`将会是约10GB的WAL。不过如前所述,max_wal_size是2 - 3个检查点的配额,所以max_wal_size = 30GB(3 x 10GB)似乎是正确的。
|
|
||||||
|
|
||||||
其他方法使用不同的数据来源,但想法是相同的。
|
|
||||||
|
|
||||||
## 传播检查点
|
|
||||||
|
|
||||||
我建议你需要调整`checkpoint_timeout`和`max_wal_size`,但我并没有说出全部的真相。还有另一个参数叫做`checkpoint_completion_target`。但要调整它,你需要了解“传播检查点”是什么意思。
|
|
||||||
|
|
||||||
但在CHECKPOINT期间,数据库需要执行以下三个基本步骤:
|
|
||||||
|
|
||||||
* 识别共享缓冲区中的所有脏(已修改)块
|
|
||||||
* 将所有这些缓冲区写入磁盘(或者写入文件系统缓存)
|
|
||||||
* `fsync`将所有修改后的文件保存到磁盘
|
|
||||||
|
|
||||||
只有当所有这些步骤完成后,检查点才算完成。您可以尽可能快地完成这些步骤,即一次性写入所有脏缓冲区,然后在文件上调用fsync,实际上这就是直到PostgreSQL 8.2一直做的事情。但是由于填充文件系统缓存、喂饱设备会导致I/O延迟,并影响用户会话。
|
|
||||||
|
|
||||||
为了解决这个问题,PostgreSQL 8.3引入了“扩展检查点”的概念,而不是一次写入所有的数据,写入的时间很长。这使得操作系统有时间在后台刷新脏数据,使得最终的fsync开销更小。
|
|
||||||
|
|
||||||
这些写入是基于进入下一个检查点的进度而被限制的, 数据库知道下一个检查点还剩多少时间/WAL空间,并计算出应当写出多少个缓冲区。然而数据库一直拖到最后时刻才写入。这意味着最后一批写入仍然在文件系统缓存中,使得最终的fsync()调用(在开始下一个检查点之前发出)再次开销巨大。
|
|
||||||
|
|
||||||
所以数据库需要留出足够的时间,以便脏数据在后台刷新到磁盘 - 而页缓存(Linux文件系统缓存)的到期通常是由时间驱动的,特别是通过这个内核参数:
|
|
||||||
|
|
||||||
`vm.dirty_expire_centisecs = 3000`
|
|
||||||
|
|
||||||
这表示数据在30秒后过期(默认情况下)。
|
|
||||||
|
|
||||||
注意:当涉及到内核参数时,调整`vm.dirty_background_bytes`很重要。在具有大量内存的系统上,默认值太高,从而使内核在文件系统缓存中积累了大量脏数据。内核通常决定一次刷新它们,减少扩展检查点的好处。
|
|
||||||
|
|
||||||
现在回到`checkpoint_completion_target = 0.5`。这个配置参数表示如果所有的写操作都完成的话,到下一个检查点有多远。例如,假设检查点仅由checkpoint_timeout = 5min触发,数据库将会限制写入操作,以便在2.5分钟后完成最后一次写入操作。然后操作系统又有2.5分钟将数据刷新到磁盘,这样5分钟后发出的fsync呼叫廉价又快捷。
|
|
||||||
|
|
||||||
当然,离开系统2.5分钟可能看起来过多,考虑到到期超时只有30秒。您可能会增加`checkpoint_completion_target`例如到0.85这将使系统大约45秒,比它需要的30秒多一点。这并不是推荐的,因为在密集写入的情况下,检查点可能比5分钟之后更早地被max_wal_size触发,使操作系统少于30秒。
|
|
||||||
|
|
||||||
但是,处理写入密集型工作负载的系统不太可能运行更高的checkpoint_timeouts值,使默认的completion_target值肯定太低。例如,如果将超时设置为30分钟,则会强制数据库在前15分钟内完成所有写入(以写入速率的两倍),然后闲置15分钟。
|
|
||||||
|
|
||||||
相反,您可以尝试使用此公式粗略设置checkpoint_completion_target
|
|
||||||
|
|
||||||
`(checkpoint_timeout - 2min)/ checkpoint_timeout`
|
|
||||||
|
|
||||||
其中30分钟约为0.93。有时候建议不要超过0.9--这可能是好的,你不可能观察到这两个值之间的任何显着差异。 (当使用非常高的checkpoint_timeout值时,这可能会改变,PostgreSQL 9.6现在可以达到1天)。
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 总结
|
|
||||||
|
|
||||||
所以现在你应该知道检查点的目的是什么,也是调整它们的基础。总结一下:
|
|
||||||
|
|
||||||
* 大多数检查点应该是基于时间的,即由checkpoint_timeout触发
|
|
||||||
* 性能(不频繁的检查点)和恢复所需的时间(频繁的检查点)
|
|
||||||
* 15-30分钟之间的数值是最常见的,上升到1h也不会有什么不好的,9.6甚至能设置为1天。
|
|
||||||
* 在决定超时之后,通过估计WAL的数量来选择`max_wal_size`
|
|
||||||
* 设置`checkpoint_completion_target`,以便内核有足够的时间将数据刷新到磁盘(但不是非常多)
|
|
||||||
* 还要调整`vm.dirty_background_bytes`来防止内核在页面缓存中积累大量脏数据
|
|
@ -1,23 +0,0 @@
|
|||||||
# PostgreSQL Memory Tunning
|
|
||||||
|
|
||||||
— effective_cache_size(integer)
|
|
||||||
|
|
||||||
设置计划程序关于可用于单个查询的磁盘高速缓存的有效大小的假设。 这是估算使用指数成本的因素; 更高的值使得更有可能使用索引扫描,更低的值使得更有可能使用顺序扫描。 当设置这个参数时,你应该考虑PostgreSQL的共享缓冲区和内核磁盘缓存中将用于PostgreSQL数据文件的部分。 另外,考虑到不同表上的并发查询的预期数量,因为它们将不得不共享可用空间。 此参数对PostgreSQL分配的共享内存大小没有影响,也不会保留内核磁盘缓存; 它仅用于估计目的。 系统也不会假定数据在查询之间保留在磁盘缓存中。 默认值是4千兆字节(4GB)。
|
|
||||||
|
|
||||||
25% ~ 40% mem
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
`shared_buffers` (`integer`)
|
|
||||||
|
|
||||||
设置数据库服务器将使用的共享内存缓冲区量。默认通常是 128 兆字节(`128MB`),但是如果你的内核设置不支持(在initdb时决定),那么可以会更少。这个设置必须至少为 128 千字节(`BLCKSZ`的非默认值将改变最小值)。不过为了更好的性能,通常会使用明显高于最小值的设置。
|
|
||||||
|
|
||||||
如果有一个专用的 1GB 或更多内存的数据库服务器, 一个合理的`shared_buffers`开始值是系统内存的 25%。 即使很大的`shared_buffers`有效, 也会造成一些工作负载, 但因为PostgreSQL同样依赖操作系统的高速缓冲区, 将`shared_buffers`设置为超过 40% 的RAM不太可能比一个小点值工作得更好。 为了能把对写大量新的或改变的数据的处理分布在一个较长的时间段内, `shared_buffers`更大的 设置通常要求对`max_wal_size`也做相应增加。
|
|
||||||
|
|
||||||
如果系统内存小于 1GB,一个较小的 RAM 百分数是合适的,这样可以为操作系统留下足够的空间。 同时,在 Windows 上,`shared_buffers`设置得较大也不一定有效。你会发现保持相对低的设置并且更多使用操作系统高速缓存会得到更好的结果。Windows 上可用的`shared_buffers`值通常是从 64MB 到 512 MB。
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
`effective_cache_size` (`integer`)
|
|
||||||
|
|
||||||
设置规划器对一个单一查询可用的有效磁盘缓冲区尺寸的假设。这个参数会被考虑在使用一个索引的代价估计中,更高的数值会使得索引扫描更可能被使用,更低的数值会使得顺序扫描更可能被使用。在设置这个参数时,你还应该考虑PostgreSQL的共享缓冲区以及将被用于PostgreSQL数据文件的内核磁盘缓冲区。另外,还要考虑预计在不同表上的并发查询数目,因为它们必须共享可用的空间。这个参数对PostgreSQL分配的共享内存尺寸没有影响,它也不会保留内核磁盘缓冲,它只用于估计的目的。系统也不会假设在查询之间数据会保留在磁盘缓冲中。默认值是 4吉字节(`4GB`)。
|
|
@ -1,160 +0,0 @@
|
|||||||
# SSD 原理
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
《大话存储》
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 颗粒
|
|
||||||
|
|
||||||
东芝的颗粒,海力士的颗粒
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Feature
|
|
||||||
|
|
||||||
Atomic Write: 打开开关,10%性能提升。1.2x写放大
|
|
||||||
|
|
||||||
Prioritize Write : 阿里定制功能
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 温度
|
|
||||||
|
|
||||||
正常负载:60度。
|
|
||||||
|
|
||||||
Warning级别告警: 70度
|
|
||||||
|
|
||||||
温度79度超过10秒钟,开始降速。
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 统计
|
|
||||||
|
|
||||||
Power Cycle可以用于判断重启次数。
|
|
||||||
|
|
||||||
Dynamic Bad Blocks 统计坏块数目。
|
|
||||||
|
|
||||||
半年时间将动态坏块,转为静态坏块。如果超过50,通知厂家。
|
|
||||||
|
|
||||||
一个Page 32K = 32768B,Page是最小的写入单位。
|
|
||||||
|
|
||||||
一个Block 256 Page = 8M,Block是最小的擦除单位。
|
|
||||||
|
|
||||||
坏块最大导致30% 性能损失
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### SEU问题
|
|
||||||
|
|
||||||
Media Status : Healthy|Bad
|
|
||||||
|
|
||||||
SEU Flag: Normal|Correctable|Uncorrectable
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 磨损与寿命
|
|
||||||
|
|
||||||
WL Bandwidth 磨损均衡。
|
|
||||||
|
|
||||||
每十五天翻新一遍旧数据。每十分钟检查一遍最旧的是否需要翻新。
|
|
||||||
|
|
||||||
总写入量 = 主机写入量+GC带宽
|
|
||||||
|
|
||||||
Estimated Life Left: 99.840% -> 15% 适合开始迁移
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 写放大问题
|
|
||||||
|
|
||||||
写放大= 主机写入量 / 总写入量
|
|
||||||
|
|
||||||
写放大因子,> 2 完全随机写
|
|
||||||
|
|
||||||
经验值2以内。
|
|
||||||
|
|
||||||
监控动态写放大
|
|
||||||
|
|
||||||
Write Amplifier: 1.100
|
|
||||||
|
|
||||||
统计时间窗口:1s
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Lun & Channel
|
|
||||||
|
|
||||||
最多8个并发。
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Lun ,一个数据结构。
|
|
||||||
|
|
||||||
Channel: 并发通道
|
|
||||||
|
|
||||||
CRC:存储Block错误
|
|
||||||
|
|
||||||
ECC:控制器内存错误
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
32k + 1024 bit校验和。
|
|
||||||
|
|
||||||
每1024字节需要40字节校正。
|
|
||||||
|
|
||||||
掉电保护 ,只需要刷入32K 数据。
|
|
||||||
|
|
||||||
使用主板余电的功能。从12V掉到5V,5V可以再供100ms,5ms可以刷入磁盘
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 基本测试
|
|
||||||
|
|
||||||
使用fio测试
|
|
||||||
|
|
||||||
测试模型:
|
|
||||||
|
|
||||||
顺序带宽:1个并发,队列深度128
|
|
||||||
|
|
||||||
IOPS:随机4 到 8 个并发,每个并发32~64队列深度
|
|
||||||
|
|
||||||
异步IO:最大化压测底层处理能力
|
|
||||||
|
|
||||||
DirectIO:
|
|
||||||
|
|
||||||
全盘写两三遍,做满盘预处理。
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 高级IO开发
|
|
||||||
|
|
||||||
FTL:Flash Translation Layer 将传统硬盘的API翻译成闪存的API。
|
|
||||||
|
|
||||||
255个逻辑controller
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## SSD基础知识
|
|
||||||
|
|
||||||
LBA -> PBA
|
|
||||||
|
|
||||||
PCI倍速
|
|
||||||
|
|
||||||
网卡
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
730 四个插槽,每个16速。、
|
|
||||||
|
|
||||||
一块万兆网卡x8。一块四口千兆网卡。
|
|
||||||
|
|
||||||
一块存储卡x8
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
15000 *
|
|
||||||
|
|
||||||
块大小越大,造成的IO越高。
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user