PostgreSQL 单块读
前言
什么是单块读?顾名思义,就是每次读取单个 block 。
Oracle 是支持多块读,今天测试一下 PostgreSQL 是否支持多块读,虽然之前听说是不支持的,今天直接测试验证一下。
测试使用的工具为:strace 、pg 15
block_size
PostgreSQL 可以通过 block_size 设置 block 的大小。不过这个参数是只读的,在数据库安装之后就无法修改了。默认是 8K,可以在编译的时候通过选项 --with-blocksize
指定大小,另一个关于 wal 的选项 wal_block_size
最好取值相同。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
127.0.0.1:54323; wxj@postgres > select * from pg_settings where name like '%block_size%';
+-[ RECORD 1 ]----+----------------------------------------------+
| name | block_size |
| setting | 8192 |
| unit | NULL |
| category | Preset Options |
| short_desc | Shows the size of a disk block. |
| extra_desc | NULL |
| context | internal |
| vartype | integer |
| source | default |
| min_val | 8192 |
| max_val | 8192 |
| enumvals | NULL |
| boot_val | 8192 |
| reset_val | 8192 |
| sourcefile | NULL |
| sourceline | NULL |
| pending_restart | f |
+-[ RECORD 2 ]----+----------------------------------------------+
| name | wal_block_size |
| setting | 8192 |
| unit | NULL |
| category | Preset Options |
| short_desc | Shows the block size in the write ahead log. |
| extra_desc | NULL |
| context | internal |
| vartype | integer |
| source | default |
| min_val | 8192 |
| max_val | 8192 |
| enumvals | NULL |
| boot_val | 8192 |
| reset_val | 8192 |
| sourcefile | NULL |
| sourceline | NULL |
| pending_restart | f |
+-----------------+----------------------------------------------+
安装之后,block_size 无法修改
1
2
3
4
127.0.0.1:54323; wxj@postgres > set block_size=16384;
ERROR: parameter "block_size" cannot be changed
Time: 0.355 ms
127.0.0.1:54323; wxj@postgres >
block_size 8k
首先测试 block_size 为 8K 的时候情况
数据是之前 pgbench 压测的时候产生的,通过读取 pgbench 产生的数据验证 PostgreSQL 的读取动作
1
pgbench -i -s 100 -U postgres testdb
获取进程号
1
2
3
4
5
6
7
8
9
10
11
12
13
14
127.0.0.1:54323; wxj@testdb > show block_size;
+------------+
| block_size |
+------------+
| 8192 |
+------------+
(1 row)
127.0.0.1:54323; wxj@testdb > select pg_backend_pid();
+----------------+
| pg_backend_pid |
+----------------+
| 849955 |
+----------------+
新开一个窗口对进程 strace
根据得到的进程号 strace
1
/Postgres/tools/strace_pg.sh 849955
执行查询语句
1
2
3
4
5
6
7
select count(1) from pgbench_branches;
+-------+
| count |
+-------+
| 100 |
+-------+
(1 row)
检查 strace 日志
1
2
## 因为日志内容不多就直接贴图呢
vim /Postgres/stracelog/849955_read.log
可以看到每次 pread64 函数偏移量都是 8192,刚好为 block_size 大小。
block_size 16K
接下来测试一下当 block_size=16K 的情况。
获取进程号
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
127.0.0.1:54324; postgres@testdb > select pg_backend_pid();
+----------------+
| pg_backend_pid |
+----------------+
| 1391395 |
+----------------+
(1 row)
127.0.0.1:54324; postgres@testdb > show block_size;
+------------+
| block_size |
+------------+
| 16384 |
+------------+
(1 row)
新开窗口进行 strace
1
2
3
┌─[postgres@darkstarc]─[~]
└──╼ $ /Postgres/tools/strace_pg.sh 1391395
postgres 1416671 1416670 0 10:42 pts/3 00:00:00 strace -tt -Y -f -r -o /Postgres/stracelog/1391395_read.log -p 1391395
执行全表查询
1
2
3
4
5
6
7
127.0.0.1:54324; postgres@testdb > select count(1) from pgbench_accounts;
+----------+
| count |
+----------+
| 10000000 |
+----------+
(1 row)
检查 strace 日志
可以看见 pread64 函数每次的偏移量为 16384(block_size)
在表的数量稍微大点的时候可以看见还涉及到了锁,进程调用了 futex 函数
1
2
3
4
5
└──╼ $ grep futex /Postgres/stracelog/1391395_read.log
1391395<postgres> 10:43:32.700008 (+ 0.000115) futex(0x7fc443a3f138, FUTEX_WAIT_BITSET|FUTEX_CLOCK_REALTIME, 0, NULL, FUTEX_BITSET_MATCH_ANY) = -1 EAGAIN (Resource temporarily unavailable)
1391395<postgres> 10:43:33.023435 (+ 0.001178) futex(0x7fc443a3f738, FUTEX_WAKE, 1) = 1
1391395<postgres> 10:43:33.046237 (+ 0.000124) futex(0x7fc443a3f138, FUTEX_WAIT_BITSET|FUTEX_CLOCK_REALTIME, 0, NULL, FUTEX_BITSET_MATCH_ANY) = -1 EAGAIN (Resource temporarily unavailable)
1391395<postgres> 10:43:34.440591 (+ 0.000147) futex(0x7fc443a3f738, FUTEX_WAKE, 1) = 1
说明查询也会涉及锁,调用了 Linux 的 futex 函数,只是对应 postgresql 中锁的等级不同
block_size 大小对查询的影响
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
-- block_size = 8K
127.0.0.1:54323; wxj@testdb > select count(1) from pgbench_accounts;
+----------+
| count |
+----------+
| 10000000 |
+----------+
(1 row)
Time: 13835.084 ms (00:13.835)
-- block_size = 16K
127.0.0.1:54324; postgres@testdb > select count(1) from pgbench_accounts;
+----------+
| count |
+----------+
| 10000000 |
+----------+
(1 row)
Time: 2130.195 ms (00:02.130)
可以看见全表扫描大表的时候 block_size 取值 16K 的效率要比 8K 的时候高,因为扫描的 block 的数量少了
如果需要长期全表查询或者大数据量时,可以考虑调大 block_size ,单个 blok 可以容纳更多的数据,这样在读取的时候也可以减少需要扫描的 block 的数量提高效率
也不是一味的增大 block_size ,根据业务的数据类型调整
Oracle 多块读
为了有一个对比,这里贴一下 Oracle 的多块读情况
1
2
3
4
5
6
7
8
9
10
11
12
13
14
SPID SID
------------------------ ----------
756 191
SQL> select count(1) from t3;
COUNT(1)
----------
2821792
SQL> show parameter db_block_size
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
db_block_size integer 8192
可以看到在单个 block 大小为 8K 的时候,pread 函数的偏移量存在 16K、32K 的情况,说明 Oracle 在扫描数据库块的时候单次扫描 block 的数量可以超过一个
总结
PostgreSQL 目前还不支持多块读。
可以使用 uniq 简单去重看看进程在执行时涉及那些函数/操作。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
┌─[postgres@darkstarc]─[~]
└──╼ $ awk '{print $5}' /Postgres/stracelog/1391395_read.log | awk -F'(' '{print $1}' | uniq
epoll_wait
recvfrom
lseek
openat
rt_sigprocmask
ftruncate
fallocate
rt_sigprocmask
mmap
close
lseek
kill
---
rt_sigreturn
kill
---
rt_sigreturn
kill
pread64
futex
pread64
pselect6
futex
pread64
futex
pread64
futex
pread64
pselect6
pread64
epoll_wait
read
epoll_wait
---
rt_sigreturn
read
epoll_wait
---
rt_sigreturn
---
rt_sigreturn
read
munmap
unlink
sendto
recvfrom
epoll_wait