1. 程式人生 > >PgSQL · 應用案例 · 阿裏雲 RDS PostgreSQL 高並發特性 vs 社區版本

PgSQL · 應用案例 · 阿裏雲 RDS PostgreSQL 高並發特性 vs 社區版本

SQL

摘要: 背景 進程模型數據庫,需要為每個會話指派獨立的進程與之服務,在連接數非常多,且大都是活躍連接時,進程調度浪費或引入的開銷甚至遠遠大於實際任務需要的開銷(例如上下文切換,MEMCPY等),性能下降會較為嚴重。

背景

進程模型數據庫,需要為每個會話指派獨立的進程與之服務,在連接數非常多,且大都是活躍連接時,進程調度浪費或引入的開銷甚至遠遠大於實際任務需要的開銷(例如上下文切換,MEMCPY等),性能下降會較為嚴重。

PostgreSQL與Oracle Dedicate Server一樣,屬於進程模型。在非常高並發的情況下,性能會下降比較厲害,通常社區版本可以通過加連接池來解決,例如pgbouncer,但是加連接池也會帶來一些問題:

1、綁定變量無法很好的滿足,當然,PostgreSQL 11會增加類似Oracle cursor force的功能,內部將非綁定變量的SQL轉換為綁定變量。

《PostgreSQL 11 preview - 強制auto prepared statment開關(自動化plan cache)(類似Oracle cursor_sharing force)》

2、連接池會使得跳數增加,增加了延遲。

3、數據庫防火墻配置的變化。從直接控制應用端來源,變成了連接池端來源。(除非修改連接池層的代碼,做到來源IP和端口透傳)

Oracle為了解決性能問題,提出了shared server的概念,類似數據庫端的backend process pool,一個process可能服務於多個client。

PostgreSQL也可以如法炮制,比如阿裏雲RDS PG內核層面增加了內置的POOL。在高並發的情況下,性能好很多。

測試CASE

1、測試64 ~ 16384個並發

2、測試TPC-B,包含5億數據量。

3、測試logged table與unlogged table

4、測試對比社區PostgreSQL 10 與 阿裏雲PostgreSQL 10

測試環境準備

1、數據庫使用huge page

《PostgreSQL Huge Page 使用建議 - 大內存主機、實例註意》

2、修改pgbench,支持超過1000個連接的測試

《PostgreSQL 11 preview - pgbench 支持大於1000鏈接(ppoll()代替select())》

https://commitfest.postgresql.org/18/1388/

《從PostgreSQL支持100萬個連接聊起》

如果使用ppoll,則pstack pgbench可以看到類似如下信息

Thread 1 (Thread 0x7f3f4d89d840 (LWP 116621)):    
#0  0x00007f3f4ca4569d in poll () from /lib64/libc.so.6    
#1  0x00007f3f4d45a9cf in poll (__timeout=-1, __nfds=1, __fds=0x7ffcd6e13c80) at /usr/include/bits/poll2.h:46    
#2  pqSocketPoll (end_time=-1, forWrite=0, forRead=28675152, sock=<optimized out>) at fe-misc.c:1129    
#3  pqSocketCheck (conn=conn@entry=0x1b58c50, forRead=forRead@entry=1, forWrite=forWrite@entry=0, end_time=end_time@entry=-1) at fe-misc.c:1071    
#4  0x00007f3f4d45aa50 in pqWaitTimed (forRead=forRead@entry=1, forWrite=forWrite@entry=0, conn=conn@entry=0x1b58c50, finish_time=finish_time@entry=-1) at fe-misc.c:1003    
#5  0x00007f3f4d454012 in connectDBComplete (conn=0x1b58c50) at fe-connect.c:1902    
#6  PQconnectdbParams (keywords=<optimized out>, values=<optimized out>, expand_dbname=<optimized out>) at fe-connect.c:542    
#7  0x000000000040576a in doConnect ()    
#8  0x0000000000406e29 in threadRun ()    
#9  0x0000000000403a1b in main ()

3、修改系統配置,保證有足夠的fd, proc等

《PostgreSQL 10 + PostGIS + Sharding(pg_pathman) + MySQL(fdw外部表) on ECS 部署指南(適合新用戶) - 珍藏級》

4、postgresql.conf 通用配置

listen_addresses = '0.0.0.0'    
max_connections = 30000    
superuser_reserved_connections = 13    
unix_socket_directories = '/tmp,.'    
tcp_keepalives_idle = 60    
tcp_keepalives_interval = 10    
tcp_keepalives_count = 0    
shared_buffers = 32GB    
huge_pages = on    
maintenance_work_mem = 1GB    
dynamic_shared_memory_type = posix    
vacuum_cost_delay = 0    
bgwriter_delay = 10ms    
bgwriter_lru_maxpages = 500    
effective_io_concurrency = 0    
max_parallel_workers_per_gather = 0    
wal_level = minimal    
fsync = on    
synchronous_commit = on    
full_page_writes = on    
wal_buffers = 32MB    
checkpoint_timeout = 15min    
max_wal_size = 64GB    
min_wal_size = 16GB    
checkpoint_completion_target = 0.1    
max_wal_senders = 0    
random_page_cost = 1.2    
log_destination = 'csvlog'    
logging_collector = on    
log_truncate_on_rotation = on    
log_checkpoints = on    
log_connections = on    
log_disconnections = on    
log_error_verbosity = verbose       
log_timezone = 'PRC'    
autovacuum = on    
log_autovacuum_min_duration = 0    
autovacuum_freeze_max_age = 900000000    
autovacuum_multixact_freeze_max_age = 900000000    
autovacuum_vacuum_cost_delay = 0ms    
vacuum_freeze_min_age = 500000    
vacuum_freeze_table_age = 1500000000    
vacuum_multixact_freeze_min_age = 5000000    
vacuum_multixact_freeze_table_age = 1500000000    
datestyle = 'iso, mdy'    
timezone = 'PRC'    
lc_messages = 'en_US.utf8'    
lc_monetary = 'en_US.utf8'    
lc_numeric = 'en_US.utf8'    
lc_time = 'en_US.utf8'    
default_text_search_config = 'pg_catalog.english'

5、社區版本與阿裏雲版本的差異配置
native

port = 1921

aliyun

port = 1999    
shared_preload_libraries = 'pg_concurrency_control.so'    
pg_concurrency_control.query_concurrency=64    
pg_concurrency_control.bigquery_concurrency=64    
pg_concurrency_control.transaction_concurrency=64    
pg_concurrency_control.autocommit_concurrency=64

測試TPC-B

TPC-B測試SQL如下

scale=5000

\set aid random(1, 100000 * :scale)    
\set bid random(1, 1 * :scale)    
\set tid random(1, 10 * :scale)    
\set delta random(-5000, 5000)    
BEGIN;    
UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;    
SELECT abalance FROM pgbench_accounts WHERE aid = :aid;    
UPDATE pgbench_tellers SET tbalance = tbalance + :delta WHERE tid = :tid;    
UPDATE pgbench_branches SET bbalance = bbalance + :delta WHERE bid = :bid;    
INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);    
END;

logged table

1、初始化

./pgsql11/bin/pgbench -i -s 5000

2、表大小

postgres=# \dt+    
                          List of relations    
 Schema |       Name       | Type  |  Owner   |  Size   | Description     
--------+------------------+-------+----------+---------+-------------    
 public | pgbench_accounts | table | postgres | 63 GB   |     
 public | pgbench_branches | table | postgres | 216 kB  |     
 public | pgbench_history  | table | postgres | 0 bytes |     
 public | pgbench_tellers  | table | postgres | 2200 kB |     
(4 rows)

3、社區版本測試腳本如下

vi test_native.sh    
    
#!/bin/bash    
export PGHOST=/tmp    
export PGPORT=1921    
export PGUSER=postgres    
export PGDATABASE=postgres    
    
Y=32    
for ((i=1;i<=7;i++))    
do    
X=$((Y*2))    
psql -c "vacuum freeze"    
psql -c "checkpoint"    
./pgsql11/bin/pgbench -M prepared -n -r -P 3 -c $X -j 64 -T 300 > ./test_native_$X.log 2>&1    
Y=X    
done    
    
psql -c "vacuum freeze"    
psql -c "checkpoint"    
./pgsql11/bin/pgbench -M prepared -n -r -P 3 -c 8192 -j 128 -T 600 > ./test_native_8192.log 2>&1    
    
psql -c "vacuum freeze"    
psql -c "checkpoint"    
./pgsql11/bin/pgbench -M prepared -n -r -P 3 -c 16384 -j 256 -T 600 > ./test_native_16384.log 2>&1

測試方法

chmod 700 ./test_native.sh     
nohup ./test_native.sh >/dev/null 2>&1 &

5、阿裏雲版本測試腳本如下

vi test_aliyun.sh    
    
#!/bin/bash    
export PGHOST=/tmp    
export PGPORT=1999    
export PGUSER=postgres    
export PGDATABASE=postgres    
    
Y=32    
for ((i=1;i<=7;i++))    
do    
X=$((Y*2))    
psql -c "vacuum freeze"    
psql -c "checkpoint"    
./pgsql11/bin/pgbench -M prepared -n -r -P 3 -c $X -j 64 -T 300 > ./test_aliyun_$X.log 2>&1    
Y=X    
done    
    
psql -c "vacuum freeze"    
psql -c "checkpoint"    
./pgsql11/bin/pgbench -M prepared -n -r -P 3 -c 8192 -j 128 -T 600 > ./test_aliyun_8192.log 2>&1    
    
psql -c "vacuum freeze"    
psql -c "checkpoint"    
./pgsql11/bin/pgbench -M prepared -n -r -P 3 -c 16384 -j 256 -T 600 > ./test_aliyun_16384.log 2>&1

測試方法

chmod 700 ./test_aliyun.sh     
nohup ./test_aliyun.sh >/dev/null 2>&1 &

unlogged table

1、初始化

./pgsql11/bin/pgbench -i -s 5000 --unlogged-tables

2、修改數據庫配置

vi $PGDATA/postgresql.conf    
    
synchronous_commit = off    
    
pg_ctl reload

3、測試同樣的腳本

性能對比

1 logged table對比

1、TPS對比

連接數社區版本TPS阿裏雲版本TPS社區版本TPS
(過濾首尾幹擾值)
阿裏雲版本TPS
(過濾首尾幹擾值)
646921667853無幹擾無幹擾
1286921165712無幹擾無幹擾
2566296462775無幹擾無幹擾
51244595533824614154988
102435055442953702248679
204826791388813032744358
409624218269903202339086
81927064243041861134316
163845046124781002029499

1.6萬並發時,約3倍於社區版本。

2、事務整體RT對比

連接數社區版本RT阿裏雲版本RT
640.923 ms0.941 ms
1281.839 ms1.936 ms
2564.010 ms4.021 ms
51211.151 ms9.269 ms
102427.475 ms21.070 ms
204867.295 ms46.063 ms
4096127.923 ms104.689 ms
8192999.236 ms239.466 ms
163841594.185 ms577.913 ms

3、實際SQL RT對比

連接數社區版本RT阿裏雲版本RT
640.428 ms0.465 ms
1280.698 ms0.734 ms
2561.784 ms1.658 ms
5124.736 ms4.378 ms
102411.082 ms8.664 ms
204837.258 ms8.007 ms
409665.486 ms7.395 ms
8192818.411 ms6.472 ms
163841183.571 ms4.927 ms

1.6萬連接時,真實SQL響應速度約240倍於社區版本。

4、RT 標準方差對比

連接數社區版本RT DEV阿裏雲版本RT DEV
642.960 ms2.863 ms
1287.559 ms4.914 ms
2566.595 ms6.090 ms
51211.810 ms8.704 ms
102430.656 ms46.411 ms
204888.371 ms68.239 ms
4096183.815 ms140.255 ms
819220114.612 ms345.584 ms
163842404.222 ms1116.238 ms

5、建立完所有連接的耗時對比

連接數社區版本阿裏雲版本
640 s0 s
1280 s0 s
2564.8 s5 s
5128.9 s11.3 s
102418.5 s27.4 s
204836.3 s37.8 s
409673.5 s93.6 s
8192150.9 s168.6 s
16384306 s341.8 s

6、釋放完所有連接的耗時對比

連接數社區版本阿裏雲版本
640 s0 s
1280 s0 s
2560 s0 s
5120 s0 s
10240 s0 s
20480 s0 s
40960 s0 s
8192594 s9 s
1638421 s24 s

2 unlogged table對比

1、TPS對比

連接數社區版本TPS阿裏雲版本TPS社區版本TPS
(過濾首尾幹擾值)
阿裏雲版本TPS
(過濾首尾幹擾值)
649908695932無幹擾無幹擾
1288680786719無幹擾無幹擾
25669805743717076675143
51249147594235036961153
102442295458834479848910
204832147386983672944552
409623556276043150438334
819217037245242293734553
1638419612668194330273

2、事務整體RT對比

連接數社區版本RT阿裏雲版本RT
640.644 ms0.666 ms
1281.466 ms1.466 ms
2563.617 ms3.391 ms
51210.115 ms8.343 ms
102422.761 ms20.864 ms
204855.771 ms45.903 ms
4096130.195 ms107.858 ms
8192356.904 ms239.312 ms
1638466640.630 ms570.207 ms

3、實際SQL RT對比

連接數社區版本RT阿裏雲版本RT
640.475 ms0.501 ms
1280.934 ms0.854 ms
2562.109 ms1.842 ms
5124.656 ms4.587 ms
10249.837 ms8.69 ms
204836.882 ms7.928 ms
409667.513 ms7.522 ms
8192201.208 ms6.536 ms
1638465428.243 ms4.811 ms

4、RT 標準方差對比

連接數社區版本RT DEV阿裏雲版本RT DEV
642.941 ms1.767 ms
1284.445 ms2.763 ms
2565.515 ms2.775 ms
51211.424 ms4.447 ms
102428.950 ms16.575 ms
204887.051 ms52.400 ms
4096200.132 ms149.614 ms
8192403.771 ms358.461 ms
16384462277.689 ms1161.376 ms

5、建立完所有連接的耗時對比

連接數社區版本阿裏雲版本
640 s0 s
1280 s0 s
2564.9 s5.3 s
5129.4 s10.2 s
102418.5 s20.2 s
204837.6 s40 s
409675 s81.3 s
8192151.6 s168.4 s
16384312.1 s341.5 s

6、釋放完所有連接的耗時對比

連接數社區版本阿裏雲版本
640 s0 s
1280 s0 s
2560 s0 s
5120 s0 s
10240 s0 s
20480 s0 s
40963 s3 s
81926 s9 s
163843312 s27 s

小結

進程模型數據庫,需要為每個會話指派獨立的進程與之服務,在連接數非常多,且大都是活躍連接時,進程調度浪費或引入的開銷甚至遠遠大於實際任務需要的開銷(例如上下文切換,MEMCPY等),性能下降會較為嚴重。

阿裏雲RDS PG,采用與Oracle Shared Server模式類似的方案,解決了進程模式在高並發的情況下性能下降的問題。

在超過1萬個活躍並發的情況下,阿裏雲RDS PG的TPC-B測試指標依舊能夠保持15萬左右的QPS (消除幹擾項),吞吐能力是社區版本的3倍。同時,在低並發的情況下,性能不減,與社區版本相當。

具體測試結果分析:

1、阿裏雲RDS PG在高並發下,TPS相比社區版本好很多,更加平穩。

2、阿裏雲RDS PG引入了POOL機制後,響應延遲,抖動相比社區版本低了很多。

3、啟用POOL後,整個事務的RT,相比社區版本降低,使得整個處理吞吐得到提升。

4、啟用POOL機制,使得一個事務中,真正執行SQL的時間大大縮短。同時還避免了鎖等待的問題。

16384個連接,社區版本

  1.750  BEGIN;    
 21.531  UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;    
  0.745  SELECT abalance FROM pgbench_accounts WHERE aid = :aid;    
461.077  UPDATE pgbench_tellers SET tbalance = tbalance + :delta WHERE tid = :tid;    
700.583  UPDATE pgbench_branches SET bbalance = bbalance + :delta WHERE bid = :bid;    
  1.958  INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);    
408.864  END;

16384個連接,阿裏雲版本

559.291  BEGIN;    
  2.359  UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;    
  1.223  SELECT abalance FROM pgbench_accounts WHERE aid = :aid;    
  1.191  UPDATE pgbench_tellers SET tbalance = tbalance + :delta WHERE tid = :tid;    
  2.310  UPDATE pgbench_branches SET bbalance = bbalance + :delta WHERE bid = :bid;    
  0.981  INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);    
 13.695  END;

對比以上兩個版本的事務BEGIN的耗費時間、SQL執行時間的分布:

社區版本的SQL執行時間耗時更高(基本達到了500毫秒左右);

阿裏雲的PG版本,SQL執行時間非常短(基本都在1毫秒左右)。

實際的DML SQL執行越久,持鎖就越久,並發1萬多時,社區版本PG出現較多WAITING狀態,就可以說明問題。

0:00.18 postgres: postgres postgres [local] UPDATE waiting    
0:02.62 postgres: postgres postgres [local] UPDATE waiting    
0:00.15 postgres: postgres postgres [local] UPDATE waiting    
0:00.17 postgres: postgres postgres [local] UPDATE waiting    
0:00.12 postgres: postgres postgres [local] UPDATE waiting    
0:00.11 postgres: postgres postgres [local] UPDATE waiting    
..............................  
0:00.13 postgres: postgres postgres [local] COMMIT            
0:00.13 postgres: postgres postgres [local] UPDATE waiting    
0:00.13 postgres: postgres postgres [local] UPDATE waiting    
0:00.16 postgres: postgres postgres [local] UPDATE waiting    
0:00.14 postgres: postgres postgres [local] UPDATE waiting    
.....................

阿裏雲RDS PG內置POOL,不會導致SQL執行時間變長。因此有效的避免了持有資源鎖的問題,是的真實的SQL RT非常的平穩。

連接數社區版本RT阿裏雲版本RT
640.475 ms0.501 ms
1280.934 ms0.854 ms
2562.109 ms1.842 ms
5124.656 ms4.587 ms
10249.837 ms8.69 ms
204836.882 ms7.928 ms
409667.513 ms7.522 ms
8192201.208 ms6.536 ms
1638465428.243 ms4.811 ms

5、啟用POOL後,16384個連接高並發下,收尾時長縮短。從3312秒縮短到了27秒。

6、進程模式,建立連接比較耗時,如果業務上需要短時間內創建大量連接,也是一個瓶頸。比如創建16384個連接,串行創建,全部建立完16384個連接大概需要花費300秒。這樣的業務,建議采用業務層連接池,並且配置較少的後端連接。

7、pgbench在統計TPS時,從所有連接建立完成,到所有連接退出,這之間產生的TPS。當需要建立很多連接或釋放很多連接時,可能會耗時比較久,導致實際測試的性能不準,特別是在8000個連接以上時,斷開連接過程中,TPS下降比較明顯,並且會被統計進去,實測600秒,到1000多秒才完成統計,詳見LOG。

8、阿裏雲RDS PG內置POOL,相比外置連接池,還有一個好處是“不會影響綁定變量的使用,也不會引入新的跳數,同時不會影響數據庫pg_hba.conf防火墻的配置”。

在超過1萬個活躍並發的情況下,阿裏雲RDS PG的TPC-B測試指標依舊能夠保持15萬左右的QPS (消除幹擾項),吞吐能力是社區版本的3倍。真實SQL執行響應速度240倍於社區版本。同時低並發的情況下,性能不減,與社區版本相當。

原文鏈接


PgSQL · 應用案例 · 阿裏雲 RDS PostgreSQL 高並發特性 vs 社區版本