ITPub博客

首页 > 数据库 > > PostgreSQL sharding : citus 系列5 - worker节点网络优化

PostgreSQL sharding : citus 系列5 - worker节点网络优化

作者:gaokeke123 时间:2022-05-09 11:30:10 0 删除 编辑

标签

PostgreSQL , citus , pgbouncer , 网络优化 , worker节点


背景

citus 节点间的网络需求:

1、cn节点访问所有worker节点。oltp业务的访问较频繁。

2、重分布数据时,worker节点间相互访问。访问频度不大,OLAP业务常见,一旦有可能数据交换吞吐较大。

citus的cn节点连worker节点为有两种模式,

一种为事务级保持连接模式(每条SQL发起时建立连接,SQL结束后释放连接(除非在事务中,否则SQL结束立即释放连接)。),

另一种为会话级保持连接模式(会话发起时建立连接,会话结束后释放连接。)。

1、跑OLAP类的SQL时,使用的是第一种即时连接模式(OLAP场景并发不高,建立连接带来的额外开销不明显)

可以在worker节点打开参数进行跟踪

  1. postgres=# show log_connections ;
  2. log_connections
  3. -----------------
  4. on
  5. (1 row)
  6. postgres=# show log_disconnections ;
  7. log_disconnections
  8. --------------------
  9. on
  10. (1 row)

例子,

以下两条SQL均为即时短连接模式(Custom Scan (Citus Task-Tracker) Custom Scan (Citus Real-Time))。

  1. postgres=# set citus.task_executor_type =task;
  2. ERROR: invalid value for parameter "citus.task_executor_type": "task"
  3. HINT: Available values: real-time, task-tracker.
  4. postgres=# set citus.task_executor_type ='task-tracker';
  5. SET
  6. postgres=# explain select count(*) from pgbench_accounts ;
  7. QUERY PLAN
  8. ---------------------------------------------------------------------------------------------------------------------
  9. Aggregate (cost=0.00..0.00 rows= width=)
  10. -> Custom Scan (Citus Task-Tracker) (cost=0.00..0.00 rows= width=)
  11. Task Count: 128
  12. Tasks Shown: One of 128
  13. -> Task
  14. Node: host=172.24.211.224 port=1921 dbname=postgres
  15. -> Aggregate (cost=231.85..231.86 rows=1 width=8)
  16. -> Seq Scan on pgbench_accounts_106812 pgbench_accounts (cost=0.00..212.48 rows=7748 width=)
  17. (8 rows)
  18. postgres=# set citus.task_executor_type ='real-time';
  19. postgres=# explain select count(*) from pgbench_accounts ;
  20. QUERY PLAN
  21. ---------------------------------------------------------------------------------------------------------------------
  22. Aggregate (cost=0.00..0.00 rows= width=)
  23. -> Custom Scan (Citus Real-Time) (cost=0.00..0.00 rows= width=)
  24. Task Count: 128
  25. Tasks Shown: One of 128
  26. -> Task
  27. Node: host=172.24.211.224 port=1921 dbname=postgres
  28. -> Aggregate (cost=231.85..231.86 rows=1 width=8)
  29. -> Seq Scan on pgbench_accounts_106812 pgbench_accounts (cost=0.00..212.48 rows=7748 width=)
  30. (8 rows)

2、跑OLTP查询时(通常并发很高,前端有连接池(保持会话)),为会话级保持连接模式(Custom Scan (Citus Router))。

以下SQL为长连接模式(不会立即释放,而是等会再释放,以降低高并发时连接带来的开销)

  1. postgres=# explain select * from pgbench_accounts where aid=5;
  2. QUERY PLAN
  3. ------------------------------------------------------------------------------------------------------------------------------------------
  4. Custom Scan (Citus Router) (cost=0.00..0.00 rows= width=)
  5. Task Count: 1
  6. Tasks Shown: All
  7. -> Task
  8. Node: host=172.24.211.224 port=1921 dbname=postgres
  9. -> Index Scan using pgbench_accounts_pkey_106836 on pgbench_accounts_106836 pgbench_accounts (cost=0.28..2.50 rows=1 width=97)
  10. Index Cond: (aid = 5)
  11. (7 rows)

看以上两种场景,CITUS应该说设计得已经很不错了。既能满足TP也能满足AP。

但是前面也说了,连接保持是在事务或会话层面的,如果查询量大,或者用户使用了短连接,建立连接的开销就会很凸显。

  1. newdb=> \c postgres postgres
  2. You are now connected to database "postgres" as user "postgres".
  3. postgres=# select * from pgbench_accounts where aid=1;
  4. aid | bid | abalance | filler
  5. -----+-----+----------+--------------------------------------------------------------------------------------
  6. 1 | 1 | 7214 |
  7. (1 row)
  8. Time: 11.264 ms -- 包括新建连接的开销
  9. postgres=# select * from pgbench_accounts where aid=1;
  10. aid | bid | abalance | filler
  11. -----+-----+----------+--------------------------------------------------------------------------------------
  12. 1 | 1 | 7214 |
  13. (1 row)
  14. Time: 0.905 ms -- 已建立连接

使用pgbouncer,解决建立连接的开销

pic

在worker节点上,部署pgbouncer,所有与worker节点建立的连接都通过pgbouncer连接池,以此来保持住连接,降低worker节点频繁新建连接的开销。

部署方法

1、所有worker节点

pgbouncer

yum install -y pgbouncer  

配置

  1. vi /etc/pgbouncer/pgb.ini
  2. [databases]
  3. newdb = host=/tmp dbname=newdb port=1921 user=digoal pool_size=128 reserve_pool=10
  4. [pgbouncer]
  5. logfile = /var/log/pgbouncer/pgbouncer.log
  6. pidfile = /var/run/pgbouncer/pgbouncer.pid
  7. listen_addr = 0.0.0.0
  8. listen_port = 8001
  9. auth_type = any
  10. auth_file = /etc/pgbouncer/userlist.txt
  11. pool_mode = session
  12. server_reset_query = DISCARD ALL
  13. max_client_conn = 5000
  14. default_pool_size = 128
  15. ; 最大不要超过4倍CPU数

启动

pgbouncer -d -u pgbouncer /etc/pgbouncer/pgb.ini  

在一个citus集群中,可以同时存在直连worker或者通过pgbouncer连接worker。

不同的database可以有不同的配置。

以下例子,新建一个database,使用pgbouncer连接worker.

2、所有节点(包括cn, worker)

新建数据库,插件

  1. su - postgres
  2. psql -c "create role digoal login;"
  3. psql -c "create database newdb;"
  4. psql -c "grant all on database newdb to digoal;"
  5. psql -U postgres newdb -c "create extension citus;"

cn节点

将worker添加到集群配置,使用pgbouncer的连接端口

  1. su - postgres -c "psql -U postgres newdb -c \"SELECT * from master_add_node('xxx.xxx.xxx.224', 8001);\""
  2. su - postgres -c "psql -U postgres newdb -c \"SELECT * from master_add_node('xxx.xxx.xxx.230', 8001);\""
  3. su - postgres -c "psql -U postgres newdb -c \"SELECT * from master_add_node('xxx.xxx.xxx.231', 8001);\""
  4. su - postgres -c "psql -U postgres newdb -c \"SELECT * from master_add_node('xxx.xxx.xxx.225', 8001);\""
  5. su - postgres -c "psql -U postgres newdb -c \"SELECT * from master_add_node('xxx.xxx.xxx.227', 8001);\""
  6. su - postgres -c "psql -U postgres newdb -c \"SELECT * from master_add_node('xxx.xxx.xxx.232', 8001);\""
  7. su - postgres -c "psql -U postgres newdb -c \"SELECT * from master_add_node('xxx.xxx.xxx.226', 8001);\""
  8. su - postgres -c "psql -U postgres newdb -c \"SELECT * from master_add_node('xxx.xxx.xxx.229', 8001);\""

MX配置,同样,将worker节点添加到元数据同步中。

psql newdb postgres  
  1. select * from master_add_node('xxx.xxx.xxx.224',8001);
  2. select * from master_add_node('xxx.xxx.xxx.230',8001);

开启同步到元数据。

  1. select start_metadata_sync_to_node('xxx.xxx.xxx.224',8001);
  2. select start_metadata_sync_to_node('xxx.xxx.xxx.230',8001);

测试

1、tpc-b 长连接测试

pgbench -i -s -U digoal newdb  
  1. psql -U digoal newdb
  2. select create_distributed_table('pgbench_accounts','aid');
  3. select create_distributed_table('pgbench_branches','bid');
  4. select create_distributed_table('pgbench_tellers','tid');
  5. select create_distributed_table('pgbench_history','aid');
pgbench -M prepared -v -r -P 1 -c 64 -j 64 -T 120 -U digoal newdb -S  

性能与不使用pgbouncer差不多,因为使用了长连接测试简单SQL(本身citus就使用了会话级连接保持,没有短连接问题)。

citus第一次QUERY的隐藏耗时

在一个新建的会话中,第一次查询总是需要需要耗费更多的时间(如果没有建立连接,那么包括建立连接的时间。即使已建立连接,也需要耗费额外的一些时间)(具体原因可以跟踪分析一下code)。

以下使用的是pgbouncer连接worker,因此第一次QUERY不包含建立连接的时间。

  1. newdb=> \q
  2. postgres@digoal-citus-gpdb-test001-> psql newdb digoal
  3. psql (10.5)
  4. Type "help" for help.
  5. \timing
  6. newdb=> select * from pgbench_accounts where aid=5;
  7. aid | bid | abalance | filler
  8. -----+-----+----------+--------------------------------------------------------------------------------------
  9. 5 | 1 | |
  10. (1 row)
  11. Time: 6.016 ms -- 不包括新建连接(已使用pgbouncer建立),但是也多了几毫秒
  12. -- 但是相比未使用pgbouncer,已经降低了5毫秒左右的延迟。
  13. newdb=> select * from pgbench_accounts where aid=5;
  14. aid | bid | abalance | filler
  15. -----+-----+----------+--------------------------------------------------------------------------------------
  16. 5 | 1 | |
  17. (1 row)
  18. Time: 0.989 ms

多出的几毫秒,我们社区的小伙伴邓彪、王健给出了原因如下,很多地方需要检查安装的citus版本与citus.control控制文件的版本是否兼容(比如加载分布式TABLE的RELCACHE时,第一次访问就是这个问题),不兼容报错:

详见代码

https://github.com/citusdata/citus/blob/3fa04d8f2c8f27b1377fe4c1468ee47358117e3c/src/backend/distributed/utils/metadata_cache.c

  1. /*
  2. * CheckAvailableVersion compares CITUS_EXTENSIONVERSION and the currently
  3. * available version from the citus.control file. If they are not compatible,
  4. * this function logs an error with the specified elevel and returns false,
  5. * otherwise it returns true.
  6. */
  7. bool
  8. CheckAvailableVersion(int elevel)
  9. {
  10. char *availableVersion = NULL;
  11. if (!EnableVersionChecks)
  12. {
  13. return true;
  14. }
  15. availableVersion = AvailableExtensionVersion();
  16. if (!MajorVersionsCompatible(availableVersion, CITUS_EXTENSIONVERSION))
  17. {
  18. ereport(elevel, (errmsg("loaded Citus library version differs from latest "
  19. "available extension version"),
  20. errdetail("Loaded library requires %s, but the latest control "
  21. "file specifies %s.", CITUS_MAJORVERSION,
  22. availableVersion),
  23. errhint("Restart the database to load the latest Citus "
  24. "library.")));
  25. return false;
  26. }
  27. return true;
  28. }
  29. /*
  30. * AvailableExtensionVersion returns the Citus version from citus.control file. It also
  31. * saves the result, thus consecutive calls to CitusExtensionAvailableVersion will
  32. * not read the citus.control file again.
  33. */
  34. static char *
  35. AvailableExtensionVersion(void)
  36. {
  37. ReturnSetInfo *extensionsResultSet = NULL;
  38. TupleTableSlot *tupleTableSlot = NULL;
  39. FunctionCallInfoData *fcinfo = NULL;
  40. FmgrInfo *flinfo = NULL;
  41. int argumentCount = ;
  42. EState *estate = NULL;
  43. bool hasTuple = false;
  44. bool goForward = true;
  45. bool doCopy = false;
  46. char *availableExtensionVersion;
  47. InitializeCaches();
  48. estate = CreateExecutorState();
  49. extensionsResultSet = makeNode(ReturnSetInfo);
  50. extensionsResultSet->econtext = GetPerTupleExprContext(estate);
  51. extensionsResultSet->allowedModes = SFRM_Materialize;
  52. fcinfo = palloc0(sizeof(FunctionCallInfoData));
  53. flinfo = palloc0(sizeof(FmgrInfo));
  54. fmgr_info(F_PG_AVAILABLE_EXTENSIONS, flinfo);
  55. InitFunctionCallInfoData(*fcinfo, flinfo, argumentCount, InvalidOid, NULL,
  56. (Node *) extensionsResultSet);
  57. /* pg_available_extensions returns result set containing all available extensions */
  58. (*pg_available_extensions)(fcinfo);
  59. tupleTableSlot = MakeSingleTupleTableSlot(extensionsResultSet->setDesc);
  60. hasTuple = tuplestore_gettupleslot(extensionsResultSet->setResult, goForward, doCopy,
  61. tupleTableSlot);
  62. while (hasTuple)
  63. {
  64. Datum extensionNameDatum = ;
  65. char *extensionName = NULL;
  66. bool isNull = false;
  67. extensionNameDatum = slot_getattr(tupleTableSlot, 1, &isNull);
  68. extensionName = NameStr(*DatumGetName(extensionNameDatum));
  69. if (strcmp(extensionName, "citus") == )
  70. {
  71. MemoryContext oldMemoryContext = NULL;
  72. Datum availableVersion = slot_getattr(tupleTableSlot, 2, &isNull);
  73. /* we will cache the result of citus version to prevent catalog access */
  74. oldMemoryContext = MemoryContextSwitchTo(CacheMemoryContext);
  75. availableExtensionVersion = text_to_cstring(DatumGetTextPP(availableVersion));
  76. MemoryContextSwitchTo(oldMemoryContext);
  77. ExecClearTuple(tupleTableSlot);
  78. ExecDropSingleTupleTableSlot(tupleTableSlot);
  79. return availableExtensionVersion;
  80. }
  81. ExecClearTuple(tupleTableSlot);
  82. hasTuple = tuplestore_gettupleslot(extensionsResultSet->setResult, goForward,
  83. doCopy, tupleTableSlot);
  84. }
  85. ExecDropSingleTupleTableSlot(tupleTableSlot);
  86. ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
  87. errmsg("citus extension is not found")));
  88. return NULL;
  89. }

pgbouncer连接池连接worker的好处

1、对于业务层短连接会有比较好的效果。可以降低至少5毫秒左右的延迟。

2、对于大量复杂查询(需要motion的查询),可以减少节点间的连接数。

同时也需要注意pgbouncer瓶颈:带宽,QPS(单核,单个pgbouncer进程峰值QPS约5万)

参考

man 1 pgbouncer

man 5 pgbouncer

《PostgreSQL 连接池 pgbouncer 使用》

来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/26692020/viewspace-3118694/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论
大家好

注册时间:2013-08-14

  • 博文量
    1432
  • 访问量
    31638