PostgreSQL写放大

时间:2016-10-23 16:32:03

标签: postgresql postgresql-9.6

我试图找出PostgreSQL在磁盘上施加了多大的压力,结果到目前为止令人沮丧。请看一下方法论,显然我错过了某些东西或以错误的方式计算数字。

环境

PostgreSQL 9.6.0-1.pgdg16.04 + 1在一个单独的LXC容器内运行,Ubuntu 16.04.1 LTS(内核版本4.4.0-38-generic,ext4文件系统在SSD之上),只有一个我运行测试的客户端连接。

我禁用autovacuum以防止不必要的写入。 写入字节的计算是通过以下命令完成的,我想查找所有PostgreSQL进程(包括WAL编写器)写入的总字节数:

pgrep postgres | xargs -I {} cat /proc/{}/io | grep ^write_bytes | cut -d' ' -f2 | python -c "import sys; print sum(int(l) for l in sys.stdin)"

测试

使用#符号我标记了一个数据库命令,我在数据库命令之后标记了write_bytes sum的结果。测试用例很简单:只有一个int4列的表填充了10000000个值。

在每次测试之前,我运行一组命令来释放磁盘空间并阻止其他写入:

# DELETE FROM test_inserts;
# VACUUM FULL test_inserts;
# DROP TABLE test_inserts;

测试#1:未记录的表

正如文档所述,UNLOGGED表中的更改不会写入WAL日志,因此它是一个很好的开始点:

# CREATE UNLOGGED TABLE test_inserts (f1 INT);
→ 1526276096
# INSERT INTO test_inserts SELECT generate_series(1, 10000000);
→ 1902977024

差异是 376700928字节(~359MB),这有点合理(千万个4字节整数+行,页面和其他成本),但仍然看起来有点太多,几乎是实际数据量的10倍。

测试#2:带有主键的未记录表

# CREATE UNLOGGED TABLE test_inserts (f1 INT PRIMARY KEY);
→ 2379882496
# INSERT INTO test_inserts SELECT generate_series(1, 10000000);
→ 2967339008

差异是 587456512字节(~560MB)。

测试#3:常规表

# CREATE TABLE test_inserts (f1 INT);
→ 6460669952
# INSERT INTO test_inserts SELECT generate_series(1, 10000000);
→ 7603630080

差异已经 1142960128字节(~1090MB)。

测试#4:带主键的常规表

# CREATE TABLE test_inserts (f1 INT PRIMARY KEY);
→ 12740534272
# INSERT INTO test_inserts SELECT generate_series(1, 10000000);
→ 14895218688

现在差异是 2154684416字节(~2054MB),大约30秒后写入额外的100MB。

对于这个测试用例,我按流程进行了细分:

                            Process                   | Bytes written
/usr/lib/postgresql/9.6/bin/postgres                  |             0
 \_ postgres: 9.6/main: checkpointer process          |      99270656
 \_ postgres: 9.6/main: writer process                |      39133184
 \_ postgres: 9.6/main: wal writer process            |     186474496
 \_ postgres: 9.6/main: stats collector process       |             0
 \_ postgres: 9.6/main: postgres testdb [local] idle  |    1844658176

有关如何衡量我正确寻找的价值观的任何想法和建议?也许它是一个内核错误?或者PostgreSQL真的做了这么多的写作?

编辑:要仔细检查write_bytes是什么意思,我写了一个简单的python script证明,这个值是实际写入的字节值。

编辑2:对于PostgreSQL 9.5测试用例#1显示362577920字节,测试#4显示2141343744字节,因此它与PG版本无关。

编辑3: Richard Huxton提到Database Page Layout文章,我想详细说明:我同意存储费用,包括24个字节的行标题, 4个字节的数据本身甚至4个字节用于数据对齐(通常为8个字节),每行提供32个字节,并且每行有大约320MB的行数,这是我用测试#1得到的。 我可以假设在这种情况下,主键应该与数据大小相同,并且测试#4(数据和PK)都将写入WAL。这给了360MB x 4 = 1.4GB,这比我得到的结果要差。

0 个答案:

没有答案