[DEPESZ] What is the point of bouncing?【譯】

文章目錄

原文鏈接:https://www.depesz.com/2012/12/02/what-is-the-point-of-bouncing/,譯者(guobo507)水平有限,不足之處,也就這樣了,哈哈。文中所有命令的輸出,全部照搬原文,等有時間了我會自己驗證的,各位看官請輕噴。

“What is the point of bouncing?”

有些人可能熟悉PgBouncer項目,有些人可能並不熟悉。有些人知道使用它的方式/方法/原因,另一些人則可能不知道。

我將在此博客文章中闡述PgBouncer的工作原理,爲什麼要這麼做,以及使用它的理由。

首先,讓我們從一些常規描述開始:

PgBouncer是一個連接池。它與數據庫(當然是PostgreSQL)保持着許多連接,當客戶端(應用程序)連接到數據庫時,將從它獲得一個與數據庫服務器的現成的連接。

連接就會存在或多或少的緩存。

當然,你們可能要問:這有什麼意義呢?是與數據庫服務器建立連接的速度太慢,您需要對優化建立連接的速度嗎?讓我們來看看吧。

我們必須考慮優化至少兩個層面的東西:

  • 網絡流量
  • 啓動新的PostgreSQL後端進程和連接認證的開銷

讓我們從第一個“網絡流量”開始討論。

顯然,在執行查詢操作時,PgBouncer對網絡的使用並沒什麼可以優化,因爲它不會緩存數據/查詢結果。因此,它實際上會使查詢速度變慢一些。因爲您沒有將查詢直接發送到數據庫服務器,而是將其發送給了PgBouncer,後者又會將其轉發給數據庫服務器。

幸運的是,到目前爲止,在我所見過的所有情況下,PgBouncer的查詢開銷都可以忽略不計。

但是建立連接的開銷呢?

我使用兩種不同的身份驗證方案進行了測試:trustmd5。在這兩個連接認證方案中,我都運行了psql,且沒有任何其他命令,僅僅只是“psql -h localhost -U…”發起連接。在psql啓動之前,我已經在系統中運行了tcpdump,從中我可以看到:在收到psql登錄成功的提示符後,該psql連接立即就被殺死了。

下面是trust認證方式下的測試結果:

14:41:27.603668 IP 127.0.0.1.50126 > 127.0.0.1.5920: Flags [S], seq 716127354, win 32792, options [mss 16396,sackOK,TS val 2052107 ecr 0,nop,wscale 7], length 0
14:41:27.603682 IP 127.0.0.1.5920 > 127.0.0.1.50126: Flags [S.], seq 212587636, ack 716127355, win 32768, options [mss 16396,sackOK,TS val 2052107 ecr 2052107,nop,wscale 7], length 0
14:41:27.603693 IP 127.0.0.1.50126 > 127.0.0.1.5920: Flags [.], ack 1, win 257, options [nop,nop,TS val 2052107 ecr 2052107], length 0
14:41:27.603797 IP 127.0.0.1.50126 > 127.0.0.1.5920: Flags [P.], seq 1:81, ack 1, win 257, options [nop,nop,TS val 2052107 ecr 2052107], length 80
14:41:27.603805 IP 127.0.0.1.5920 > 127.0.0.1.50126: Flags [.], ack 81, win 256, options [nop,nop,TS val 2052107 ecr 2052107], length 0
14:41:27.606109 IP 127.0.0.1.5920 > 127.0.0.1.50126: Flags [P.], seq 1:327, ack 81, win 256, options [nop,nop,TS val 2052108 ecr 2052107], length 326
14:41:27.606195 IP 127.0.0.1.50126 > 127.0.0.1.5920: Flags [.], ack 327, win 256, options [nop,nop,TS val 2052108 ecr 2052108], length 0

下面是md5認證方式下的測試結果:

14:42:24.502813 IP 127.0.0.1.50143 > 127.0.0.1.5920: Flags [S], seq 1860178418, win 32792, options [mss 16396,sackOK,TS val 2066332 ecr 0,nop,wscale 7], length 0
14:42:24.502825 IP 127.0.0.1.5920 > 127.0.0.1.50143: Flags [S.], seq 2534748821, ack 1860178419, win 32768, options [mss 16396,sackOK,TS val 2066332 ecr 2066332,nop,wscale 7], length 0
14:42:24.502834 IP 127.0.0.1.50143 > 127.0.0.1.5920: Flags [.], ack 1, win 257, options [nop,nop,TS val 2066332 ecr 2066332], length 0
14:42:24.502944 IP 127.0.0.1.50143 > 127.0.0.1.5920: Flags [P.], seq 1:81, ack 1, win 257, options [nop,nop,TS val 2066332 ecr 2066332], length 80
14:42:24.502950 IP 127.0.0.1.5920 > 127.0.0.1.50143: Flags [.], ack 81, win 256, options [nop,nop,TS val 2066332 ecr 2066332], length 0
14:42:24.504242 IP 127.0.0.1.5920 > 127.0.0.1.50143: Flags [P.], seq 1:14, ack 81, win 256, options [nop,nop,TS val 2066332 ecr 2066332], length 13
14:42:24.504296 IP 127.0.0.1.50143 > 127.0.0.1.5920: Flags [.], ack 14, win 257, options [nop,nop,TS val 2066332 ecr 2066332], length 0
14:42:24.504395 IP 127.0.0.1.50143 > 127.0.0.1.5920: Flags [P.], seq 81:122, ack 14, win 257, options [nop,nop,TS val 2066332 ecr 2066332], length 41
14:42:24.505548 IP 127.0.0.1.5920 > 127.0.0.1.50143: Flags [P.], seq 14:340, ack 122, win 256, options [nop,nop,TS val 2066332 ecr 2066332], length 326
14:42:24.541972 IP 127.0.0.1.50143 > 127.0.0.1.5920: Flags [.], ack 340, win 256, options [nop,nop,TS val 2066342 ecr 2066332], length 0

這裏,我們沒有基於“send packet, reply”來討論,我們討論的是應用程序(在我的示例中爲psql)和服務器(PostgreSQL)之間的5次(trust)和7次(md5)單向通信(one-way trips)過程。

因此,我在示例測試環境中進行了測試,可以通過測試ping本地主機(localhost),平均往返時間爲0.018ms。然後ping連接到連接到同一交換機的服務器(1000Mb/s速率,全雙工模式),結果是0.213ms。

這意味着使用“trust”連接到localhost的平均時間爲0.045毫秒,使用md5時連接到localhost的時間爲0.063毫秒;同理,連接同一網絡上的服務器主機的連接的時間分爲0.532毫秒(trust)和0.745毫秒(md5)。

這樣的連接時間聽起來可能並不多。但是實際情況是,當我們使用主鍵從相對較寬的表(例如750Bytes)中獲取單行數據時,僅僅花費了0.03ms,我們突然發現連接的開銷是從磁盤中獲取數據的20倍之多。

因此,在相對正常的情況下,當您有N臺連接到PostgreSQL數據庫的應用程序服務器時,可以通過配置本地(對於應用程序服務器)PgBouncer服務器,從而將連接時間減少約0.7ms。

看起來不錯,不是嗎?

但是還有另一個好處,PgBouncer會緩存到數據庫服務器的連接。如果您的應用程序停止使用其與服務器的連接(例如,通過與PgBouncer斷開連接),PgBouncer和PostgreSQL之間的連實際上並沒有關閉,而是被PgBouncer保留在連接池中,以重新用於應用程序的下一個連接。

這意味着在大多數的情況下(實際上是最常見的情況),我們也同時節省了連接PostgreSQl數據庫服務器時,啓動新的後端進程的開銷。因爲就PostgreSQL而言,一個後端進程僅保持與單個客戶端(PgBouncer)的連接。

那麼,這筆開銷究竟有多大?

爲了計算它,我將編寫簡單的Perl腳本,該腳本使用unix套接字循環的連接到數據庫(以限制所有可能的開銷)。讓我們看看進展如何:

#!/usr/bin/env perl
use strict;
use warnings;
use DBI;
use Time::HiRes qw( time );
use List::Util qw( min max sum );

print get_dbh()->selectall_arrayref('SELECT version()')->[0]->[0], "\n";
my @times = ();
for (1..10) {
    my $start_time = time();
    my $dbh = get_dbh();
    my $end_time = time();
    push @times, $end_time - $start_time;
}

printf "min/avg/max: %.6f/%.6f/%.6f (ms)\n", min(@times)*1000, sum(@times)*100, max(@times)*1000;

exit;

sub get_dbh {
    return DBI->connect("dbi:Pg:dbname=depesz") or die "Oops\n";
}

運行這個腳本,結果爲:

PostgreSQL 9.1.6 on x86_64-unknown-linux-gnu, compiled by gcc (GCC) 4.1.2 20080704 (Red Hat 4.1.2-44), 64-bit
min/avg/max: 2.194166/2.564168/2.914906 (ms)

說明:在之前我寫的腳本測試結果中,它僅僅花費了百萬分之一毫秒。但是esh發現了我的算術錯誤。顯然我之前做錯了,上面的腳本及結果已經修正了。顯然,開始新連接的時間並非無關緊要,它實際上是相當高的(我的測試是在幾乎沒有流量的服務器上進行的,因此在正常工作負載下,建立連接的時間可能會更長)。

從上面我寫的所有內容可以看到,使用了PgBouncer之後,我們可以以更少的時間(通常比直接連接PostgreSQL需要花費約0.7ms的時間少的多)獲得與數據庫的新的連接。

但有的人可能會說:很好,但是我可以使用持久連接,這樣我就幾乎不需要創建新的連接了。

是的,如果您有這種需求,則不需要再通過PgBouncer來縮短創建連接的時間。

但這通常也會導致一些非常有趣的情況,很多人可能已經經歷過了。

假設您的PostgreSQL實例具有500個連接,但實際上可能只有少數的幾個連接可以執行某些操作。其餘剩下的都處於空閒(IDLE)狀態,注意不是“IDLE in transaction”。Just plain, old, boring, IDLE.

爲什麼會這樣呢?因爲通常,應用程序不會一直在數據庫中運行查詢。即使看起來好像是在查詢一樣,實際上在非數據庫相關的事情上也花費了大量時間,例如從客戶端獲取請求,生成輸出,在應用程序代碼中執行各種邏輯等。數據庫查詢雖然很重要,但並非每次都在所有應用程序服務器上同時發生。

就好像下面的結果一樣:

postgres=# SELECT CASE WHEN current_query = '<IDLE>' THEN 'idle' ELSE 'non-idle' END AS state, COUNT(*), SUM(clock_timestamp() - query_start) FROM pg_stat_activity GROUP BY 1;
state   | COUNT |            SUM
----------+-------+----------------------------
idle     |   647 | 106 days 2298:57:58.271602
non-idle |     5 | 00:00:05.509914
(2 ROWS)

在這個數據庫(這是在線零售商的主要生產數據庫)中,我們檢查發現,我總共有5個正常工作的後端進程,總的查詢持續時間爲5秒,還有647個空閒的後端進程,總空閒時間爲106天。

而且,所有這些處於IDLE狀態的後端進程都在使用內存。當然,可能沒有操作系統的ps命令報告內存使用情況的那麼多。就我的情況而言,在這600個後端上,如果我可以關閉所有那些空閒,無用的後端進程,則可以釋放約2GB的RAM。

這就是PgBouncer很棒的原因。那麼,它是怎麼做到的呢?

通常,管理連接的流程如下:

  • 客戶端連接到PgBouncer,從而建立客戶端連接;
  • PgBouncer從池中獲得一個免費連接,或建立一個新連接,並將該服務器連接分配給該客戶端連接;
  • 一旦客戶端連接成功,它與PgBouncer的連接(客戶端連接)就會與PgBouncer和PostgreSQL之間的相同連接(服務器連接)進行配對;
  • 當客戶端斷開連接時,它的客戶端連接當然會關閉掉,但是PgBouncer與PostgreSQL服務器的連接會放回連接池中。

到目前爲止,你會覺得一切都很好,乾淨利落。

但是在PgBouncer.ini配置文件中,您可以找到一個配置選項:“pool_mode”,它可以具有三個可能的值:

  • session
  • transaction
  • statement

會話池(session)是默認的設置,它的工作邏輯就像我上面剛剛描述的那樣。另外兩個設置的工作邏輯會更加聰明(Smarter)。

事務池(transaction)意味着客戶端連接和服務器連接之間的配對不是恆定的。當客戶端啓動新事務時,服務器連接就會分配給客戶端連接,並不會在客戶端斷開連接時才釋放到池中,而是在完成事務時釋放(例如,執行了COMMIT或者ROLLBACK)。

立刻尖叫:好極了!因此,所有那些閒置的事務都將消失。對?好吧,是的,但也不是。在開始更多細節之前,讓我們先談談語句池(statement)。您可以根據事務池的描述和名稱來猜測,在語句池中,服務器的連接在任何語句被執行後,就會立即被放回到連接池中,因爲每條語句都會被自動提交(AUTOCOMMIT)。

如果您不是經驗豐富的PostgreSQL用戶,您可能會說您通常不使用事務,只會在沒有任何BEGIN/COMMIT的情況下執行語句,因爲它們很慢。

如果那是您,請不用擔心;或者說,我非常抱歉。PostgreSQL中的每個查詢,即使沒有被顯式的BEGIN; COMMIT;包圍,它仍然是一個事務。它們是很小/很短的事務,就在語句執行期間存在。記住,事務總是存在的。

所以,您可以在PgBouncer.ini中啓用交易池,然後基本上就可以擺脫大多數空閒(IDLE)狀態的後端進程了,對嗎?是的,當然了。

在我們正在使用的其中一臺服務器上,使用事務池的設置,我們能夠觀察到顯著的效果改善。比如從:

  • 每天平均500個併發連接
  • 一天中的高峯時段達到2000個連接

到:

  • 日平均:40
  • 峯值:60

注意,即使這樣,我們發現仍然會有空閒連接數多於非空閒數的情況,例如:

$ SELECT CASE WHEN current_query = '<IDLE>' THEN 'idle' ELSE 'non-idle' END AS state, COUNT(*), SUM(clock_timestamp() - query_start) FROM pg_stat_activity GROUP BY 1;
state   | COUNT |       SUM
----------+-------+-----------------
idle     |    20 | 00:12:23.771483
non-idle |     6 | 00:00:00.178652
(2 ROWS)

但這是因爲PgBouncer必須在池中保存一些連接。

請相信我,這是一個非常繁忙的數據庫服務器。它的主要數據庫是Internet上一個非常大的平臺,即使到現在(在美國東海岸的波蘭,下午4點,週日上午10點,星期日),該數據庫每秒仍具有約3000筆交易。

減少後端進程的數量的好處是雙重的。當然,您可以獲得更多的可用內存,內核可以將其用作磁盤緩存,並且您也減少了PostgreSQL自身內部進程間通信的開銷。新版本的PostgreSQL中,進程之間的開銷控制會更好,但是我已經遇到過較早的PostgreSQL的情況,其中1000個後端進程之間的IPC極大地減慢了一切。

那麼事務池是萬金油(原文爲:sliver bullet)嗎?是任何情況下都可以使用的設置嗎?很不幸,不行。儘管使用事務池有很多好處(我沒有使用語句池,因爲我看不出什麼時候纔是有益的),但是使用它實際上是有問題的。

原因很簡單:沒有辦法保證兩個後續的事務(甚至是查詢,如果您使用的是自動提交模式的話。)將在同一個後端進程上運行。

意味着,如果您正在跨事務的使用非事務中的數據,則可能會看到失敗或錯誤。稍後您會看到它們。

但是誰會使用非事務的數據?答案是,幾乎每個人。

例如;對於初學者而言,序列;緩存的查詢計劃;Advisory locks。還有,如果使用的是PL/Perl的函數,則爲%_SHARED

這是否意味着您不能使用它們其中任何一個?不,您可以使用,但是僅當您將狀態保持爲單個事務時使用。

例如,讓我們談談序列吧。

如果您像這樣使用它們:

$ BEGIN;
$ INSERT INTO z (x) VALUES (1);
$ INSERT INTO z2 (z_id) VALUES ( currval('z_id_seq') );
$ commit;

意思是,您從剛剛更新的序列中獲取當前值,並且在同一事務中完成,這樣是沒問題的。

但是,如果您是下面這樣:

$ BEGIN;
$ INSERT INTO z (x) VALUES (1);
$ commit;
$ BEGIN;
$ INSERT INTO z2 (z_id) VALUES ( currval('z_id_seq') );
$ commit;

您會收到一個“data corruption”的錯誤,因爲第二個事務不可避免地會在某個時間點運行在與第一個事務不同的後端,所以您可能會收到有關未初始化序列的錯誤,或者會得到錯誤的currval()值。

同理,如果您使用自動提交運行兩個插入,而沒有使用BEGIN; ... ; COMMIT;塊來包含語句,就會發生同樣的事情。

當然,對於每個問題,都會有解決方案。對於序列問題,您可以在一次事務中使用它們;或使用INSERT INTO…RETURNING ID;”並在您的應用程序中重複使用此ID,而無需調用currval();又或者首先通過select nextval(‘z_id_seq');,然後在所有後續查詢中使用此值。簡單吧。

當我們考慮prepared statements語句的情況時,它會更加複雜。

不幸的是(基於幾種情況,我認爲)在某些數據庫驅動程序中,它們已成爲默認設置,例如在Perl中的DBD::Pg中。或PHP中的PDO中。

如果您的應用程序正在使用prepared statements(注意,這實際上無關應用程序中的prepare_statement()之類的調用),您會在PostgreSQL的日誌中看到類似“parse some_identifier: SELECT …",或者“bind some_identifier: …",又或者“execute some_identifier: …"的內容。

根據您使用的程序語言/驅動程序,有不同的處理方法。在Perl中,當創建新的數據庫連接時,我會將pg_server_prepare選項設置爲0,並每次當我認爲有人嘗試將此設置變爲默認時,都會想罵人。

在PDO中,解決方案是通過將PDO::ATTR_EMULATE_PREPARES設置爲TRUE作爲第4個參數傳遞給新PDO()來啓用所謂的仿真準備( emulated prepares)。

如果您使用了其他語言,那麼我不太確定處理的方法。因爲我不得不處理這兩個語言的這個問題,並且已經解決了問題。

如果真的要使用prepared statements,該怎麼辦?而且,順便說一下,它又有什麼問題呢?

它的問題在於即使在事務之外,prepared statements也將留在後端進程。因此,您可能會看到類似如下的內容:

$ PREPARE ins(int4, int4, int4, int4, int4) AS INSERT INTO i (j) VALUES ($1),($2),($3),($4),($5);

$ BEGIN;
$ EXECUTE ins(1,2,3,4,5);
$ commit;

$ BEGIN;
$ EXECUTE ins(6,7,8,9,10);
$ commit;

$ BEGIN;
$ EXECUTE ins(11,12,13,14,15);
$ commit;

而且,當您在PgBouncer中使用事務池模式進行嘗試時,遲早會得到如下的錯誤:

ERROR:  prepared statement "ins" does NOT exist

或者:

ERROR:  prepared statement "ins" already EXISTS

因爲您會嘗試在給定的後端進程中準備該語句之前執行該語句(準備工作(preparation)在不同的事務中並且最終會在不同的進程中完成),或者您將嘗試準備一個語句,但是由於由另一個客戶端進程的已完成的工作,它可能已經在給定的後端進程中準備了。

幸運的是PgBouncer具有“重置查詢(RESET QUERY)”的概念,它其實是一個特殊查詢,在當服務器連接返回到池時會在後端進程上執行。通常,這會清除所有修改的設置,並會刪除已經準備好的語句。

您也可以考慮使用pre_prepare。您可以使用該工具來確保由PgBouncer啓動的所有後端進程都具有一些預定義的準備好的語句集供您使用。

就個人而言,我只是從應用程序中刪除對prepare statements的使用。

在極少數情況下,使用準備好的語句如果確實可以使您的應用程序受益。在這種情況下,請繼續使用它們。但是,請仔細衡量所有優點和缺點。如果對於單個用例,也許您直接連接到PostgreSQL效果會更好。

現在我們知道了PgBouncer的原理以及作用。因此,我們來討論一下它的最佳配置選項是什麼,以及如何進行監控。

當然,第一個問題是:我應該部署一個集中的PgBouncer,還是爲每個應用程序服務器都配置自己的PgBouncer服務?答案是:爲什麼不兩者兼而有之?

請認真的考慮以下幾種情況:

  • 應用程序使用持久化的連接來連接到本地PgBouncer;
  • 應用服務器上的PgBouncer,連接到集中式PgBouncer;
  • 集中式PgBouncer連接到數據庫

這樣做的好處是:

  • 由於持久化的連接,我們將啓動新連接的開銷設置爲最小;
  • 具有本地PgBouncer意味着通常應用程序不必等待獲取數據庫連接;
  • 所有應用服務器中的PgBouncer通過集中的、“idle-removing”的PgBouncer來連接。

讓我們考慮一下,在我們的例子中,我們有10個Web服務器,每個服務器運行30個生成網頁的進程。每一個進程都可能需要數據庫服務器。總共可能需要300個連接。

首先,限制每個Web服務器上的PgBouncer將傳出的連接數量,從30個限制爲4-5個。但這意味着數據庫最終可能將受到40-50個連接(每個Web服務器的4-5個連接)的攻擊。So the centralized pgBouncer removes some more idles by “juggling" server connections between connections coming from different webservers.(這一句沒想好怎麼翻譯比較合適。)

由於有了這樣的設置,我們很可能只需要在集中的PgBouncer服務器上運行10-15個到PostgreSQL的連接即可。

當您將PgBouncer安裝好以後,遲早您會遇到一個問題:我已經與數據庫建立了連接,但是我需要找到它的來源,因爲它正在做錯誤的事情。

但是當你像我上面描述的情形一樣,部署了集中式的PgBouncer時,很顯然最終所有的連接都來自於它。那麼如何找到某一個連接實際上是從哪裏發起的呢?幸運的是,找到它並不是那麼複雜,只是有點乏味。但是,在Linux上請不要使用UNIX套接字發起連接,請始終使用TCP來連接。

顯然,在Linux上,我們無法確定給定UNIX套接字連接的發起者。

我在ServerFault上諮詢了這個問題,在Linux上唯一可行的解​​決方案可能會涉及到自定義的內核構建,並在/proc/kcore(基本上是系統的整個內存)上運行gdb(調試器)。對一般用戶而言或許並不友好。

由於這個問題,我通常不使用UNIX套接字,而對它保持懷疑的態度。

無論如何,讓我們考慮以下的示例。我有這個連接:

$ SELECT * FROM pg_stat_activity WHERE procpid = 5225;
-[ RECORD 1 ]----+------------------------------
datid            | 16416
datname          | some_db
procpid          | 5225
usesysid         | 136689798
usename          | depesz
application_name |
client_addr      | 127.0.0.1
client_hostname  |
client_port      | 36740
backend_start    | 2012-12-02 16:03:17.214369+00
xact_start       | 2012-12-02 16:03:25.775003+00
query_start      | 2012-12-02 16:03:25.775003+00
waiting          | f
current_query    | SELECT pg_sleep(123124);

如您所見,它正在運行很長時間的pg_sleep(),我想找到是什麼應用程序/用戶啓動了它。我發現連接是來自127.0.0.1,從端口號(client_port)可以看出這一點:

=$ sudo netstat -ntp | grep 36740
tcp        0      0 127.0.0.1:36740             127.0.0.1:5432              ESTABLISHED 23028/pgbouncer
tcp        0      0 127.0.0.1:5432              127.0.0.1:36740             ESTABLISHED 5225/postgres: depe

所以,我們發現連接來自PgBouncer。現在,我需要連接到Pgbouncer指定的數據庫。這是通過下面的命令連接到Pgbouncer的端口並指定使用pgbouncer數據庫來完成的:

$ psql -U postgres -h localhost -p 6543 -d pgbouncer

在pgbouncer數據庫中,我可以運行SHOW SOCKETS命令。完整的輸出很長,所以讓我們看一下代表我的連接的行:

TYPE |   USER    | DATABASE  |   state   |      addr      | port  |  local_addr  | local_port |    connect_time     |    request_time     |      ptr       |      link      | recv_pos | pkt_pos | pkt_remain | send_pos | send_remain | pkt_avail | send_avail·
------+-----------+-----------+-----------+----------------+-------+--------------+------------+---------------------+---------------------+----------------+----------------+----------+---------+------------+----------+-------------+-----------+------------
S    | depesz    | some_db   | sv_active | 127.0.0.1      |  6666 | 127.0.0.1    |      36740 | 2012-12-02 16:03:17 | 2012-12-02 16:03:17 | 0x5012530      | 0x2ab9b676e1a0 |        0 |       0 |          0 |        0 |           0 |         0 |          0

第一列顯示了它是服務器連接。然後,有許多列現在對您來說或多或少,或無關緊要。但是請記住“ptr”和“link”列很重要。

多虧了它,我可以找到客戶端連接:

TYPE |   USER    | DATABASE  |   state   |      addr      | port  |  local_addr  | local_port |    connect_time     |    request_time     |      ptr       |      link      | recv_pos | pkt_pos | pkt_remain | send_pos | send_remain | pkt_avail | send_avail·
------+-----------+-----------+-----------+----------------+-------+--------------+------------+---------------------+---------------------+----------------+----------------+----------+---------+------------+----------+-------------+-----------+------------
C    | depesz    | some_db   | cl_active | 192.168.1.19   | 34368 | 192.168.1.10 |       6543 | 2012-12-02 16:03:17 | 2012-12-02 16:03:25 | 0x2ab9b676e1a0 | 0x5012530      |        0 |       0 |          0 |        0 |           0 |         0 |          0
S    | depesz    | some_db   | sv_active | 127.0.0.1      |  6666 | 127.0.0.1    |      36740 | 2012-12-02 16:03:17 | 2012-12-02 16:03:17 | 0x5012530      | 0x2ab9b676e1a0 |        0 |       0 |          0 |        0 |           0 |         0 |          0

請注意,C的連接的“ptr”列與S連接的“link”相同,並且C連接的“link”與S連接的“ptr”相同。

在C連接(客戶端連接)一行中,我看到客戶端從IP地址爲192.168.1.19的計算機連接,在此計算機上,它使用的是TCP端口34368。通過這些信息,我就可以SSH到該遠程計算機,並結合netstat命令來檢查它到底是什麼。

不幸的是PgBouncer不允許您過濾數據,SHOW SOCKETS(和其他SHOW命令一樣)只是將數據轉儲到您的應用程序,您必須自己解析/過濾想要的數據。這是可行的,儘管還可以改進。

最後一件事。我的一些讀者可能會問:pgPool呢?畢竟,畢竟它的名字帶有“pool”,它是“pooling”解決方案吧?

好吧!首先,我對pgPool的經驗很少。至少在幾年前,我測試了一下,然後我發現:

  • pgPool爲每個客戶端連接創建了新進程(PgBouncer使用單個進程和async-IO);
  • 它僅在會話池模式下有效,我不喜歡它。與會話池相比,我更喜歡持久連接。

基於這些事實,我認爲使用pgPool作爲連接池解決方案對我來說是不合理的。但是,那是在幾年前,或許現在它可能已經改變了。另一方面,pgPool似乎走了不同的路線。例如,基於語句級別的複製、自動負載平衡以及故障轉移,因此這並不是一個真正公平(apples-to-apples)的比較。

我希望以上帖子對大家有幫助,如果有任何不清楚的地方。首先,很感謝您的評論;然後,我會嘗試回答您的問題。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章