文章

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

strace-849955 可以看到每次 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 日志

strace-1391395 可以看见 pread64 函数每次的偏移量为 16384(block_size)

在表的数量稍微大点的时候可以看见还涉及到了锁,进程调用了 futex 函数 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

Oracle-多块读 可以看到在单个 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

参考

fetux1

fetux2

block_size

教你区分 多块读、单块读、散列读、顺序读、索引扫描

OS / linux / 互斥锁实现原理(futex)

本文由作者按照 CC BY 4.0 进行授权

© TheDarkStarJack. 保留部分权利。

[本站总访问量次] [本站访客数人次] [本文总阅读量次]

本站采用 Jekyll 主题 Chirpy