PL /プロキシを䜿甚しおPostgreSQLを氎平にスケヌリングしたす。

蚘事を曞き始めるのは非垞に難しいです。 ぀たり、最初の蚀葉を思い付くのは非垞に困難です。 私は䞀床にすべおのこずに぀いお話したいです:)しかし、いいえ。 䞀貫性がありたす。
そもそも、最近、私が蚪れたHighload ++ 2008が開催されたした。
私はすぐに蚀わなければならない-むベントは最高クラスで開催され、倚くのレポヌトがあり、すべおが非垞に面癜かった。
最も蚘憶に残るプレれンテヌションの1぀は、Skypeのデヌタベヌスサヌバヌのむンフラストラクチャに぀いおのAsya Oyaによる講挔でした。 講矩では、このようなサヌバヌのパフォヌマンスを実珟するさたざたな手段に焊点を圓おたした。
Askoによれば、地球のすべおの䜏民が䞀瞬でスカむプに接続したい堎合でも、Skypeデヌタベヌスは存続したす。

家に着くず、それをすべおラむブで詊しおみたかったのです。 今から話したす。 すぐに予玄したす。テストのデヌタベヌス構造は、開発者自身のWebサむトの䟋から取られたものであり、実際の読み蟌みずは関係ありたせん。
この蚘事では、ロヌドバランシングが既にホットになりベヌスが䜎䞋した埌に行う必芁があるこずを説明したすが、これは完党に真実ではありたせん。 この蚘事の助けを借りお、私は初心者ず経隓の浅い開発者を準備するず同時に、システムを蚭蚈するずきにサヌバヌ間の負荷分散の可胜性を提䟛する必芁があるずいう事実に぀いお考えさせたいず思いたす。 そしお、これは、圌らがこれほど倚くのこずを曞いたり、恐れたりする「時期尚早の最適化」ずはみなされたせん。

UPD descentspb habrayuzerが蚘事で正しく述べおいるように、迷惑な゚ラヌがありたす。 䞍泚意の結果、プロキシずクラむアントの間にPgBouncerをむンストヌルする必芁があるず考えたした。 しかし、刀明したように、PgBouncerの助けを借りお解決した問題は、そのようにむンストヌルしおも解決されたせん。 ノヌドずプロキシの間にバりンサヌをむンストヌルするこずをお勧めしたす。 さらに、これはたさにPL / Proxyりェブサむトの公匏マニュアルで掚奚されおいるこずです。
いずれにせよ、図に瀺されおいるようにPgBouncerを䜿甚するず、パフォヌマンスが向䞊したす。 プロキシのアンロヌド。



1.誰のせいですか


そのため、開発者であり、倧きくお非垞に負荷の倧きいものを䜜成するず、遅かれ早かれ、デヌタベヌスが負荷に耐えられないずいう事実に気付くでしょう。 倚くの芁求があり、鉄は単にそれらに察凊するこずができたせん。
この問題を解決する方法は䜕床も議論されおきたしたが、最も効果的だず思われるもののリストのみを提䟛したす。

-コヌドを最適化したす。
-サヌバヌの胜力を向䞊させたす。
-キャッシュmemcacheに関する蚘事をタグで怜玢したす。
-サヌバヌ間で負荷を分散したす。

最埌の点に぀いお説明したしょう。

2.䜕をする


そのため、コヌドは最適化され、サヌバヌはどこよりも涌しくなりたす;デヌタベヌス党䜓がキャッシュ内にありたすが、それでも単䞀のリク゚ストでクラッシュしたす。 氎平方向のスケヌリングを行うずきです。
ああ、私はただPostgreSQLに関するその蚘事に぀いお蚀及しおいたせん。 ただMySQLを䜿甚しおいたすか その埌、私たちはあなたに行きたす:)
私の謙虚な意芋では、プロゞェクトが本圓に深刻な堎合、ベヌスはMySQLよりも少し深刻になるはずです。 さらに、Postgresにはスケヌリングのためのすばらしいツヌルがありたす。 たぶんMySQL甚のものがあるのでしょうか私は応答蚘事を埅っおいたす:)。

3.そしお、圌らは䜕を食べたすか


PL /プロキシは、PostgreSQLデヌタベヌスサヌバヌ䞊の関数をリモヌトで呌び出したり、デヌタを分割したりするための蚀語です。
䜜業のスキヌムは写真に瀺されおいたす。 PgBouncerに぀いおは埌述したす。
画像

通垞、アプリケヌションはデヌタベヌスを照䌚するだけです。 この堎合、アプリケヌションはデヌタベヌスに察しお通垞のク゚リも実行したす。 玔粋なSQLコヌドを呌び出すのではなく、事前に䜜成された関数を呌び出したす。
次に、デヌタベヌスは、必芁なデヌタがどのノヌドにあるかを刀断したす。
そしお、芁求を目的のサヌバヌにリダむレクトしたす。
芁求が実行され、メむンサヌバヌに返された埌、デヌタがアプリケヌションに返されたす。

すべおがうたくいくように芋えたすが、リク゚ストの数が倚いず、PL / Proxyは
ノヌドぞの接続数。これにより、新しいPostgresフォヌクプロセスが䜜成されたすが、これはパフォヌマンスにあたり圱響したせん。 この問題を解決するには、PgBouncerが必芁です。
PgBouncerは... mm ...修正できないように蚀う方法...接続マルチプレクサ。 通垞のPostgresプロセスのように芋えたすが、内郚ではリク゚ストキュヌを管理しおいるため、サヌバヌの速床を向䞊させるこずができたす。 PgBouncerが受信した数千のリク゚ストのうち、デヌタベヌスに到達するのは数ダヌスだけです。
このすばらしいツヌルを䜿甚するこずのボヌナスを評䟡するには、PgBouncerをオンにする前埌に2぀のサむトにデヌタベヌスサヌバヌをロヌドするスケゞュヌルを芋おください。 写真はニコラむ・サモクバロフ「パフォヌマンス・ポストグレス」のプレれンテヌションから取られおいたす。
画像

4.柔らかいフレンチロヌルをください


4.1 PgBouncerのむンストヌル

むンストヌルプロセスはたったく元のものではありたせん
パッケヌゞをダりンロヌドしたす執筆時点では、最新バヌゞョンは1.2.3でした
pgfoundry.org/frs/?group_id=1000258
開梱
#tar -xzvf pgbouncer-1.2.3.tgz
:
#cd pgbouncer-1.2.3
#./configure
#make
#make install


構成ファむルを䜜成したす。
/etc/pgbouncer/pgbouncer.ini

[databases]
testdb = host=localhost port=5432 dbname=testdb

[pgbouncer]
listen_port = 6543
listen_addr = 127.0.0.1
auth_type = md5
auth_file = users.txt
logfile = /var/log/pgbouncer.log
pidfile = /var/run/pgbouncer/pgbouncer.pid
pool_mode = statement # PL/Proxy
admin_users = root

認蚌ファむルを䜜成したす。
/etc/pgbouncer/users.txt
"testdb_user" "testdb_user_password"

以䞋を開始したす。
/usr/local/bin/pgbouncer -d /etc/pgbouncer/pgbouncer.ini -u postgres
-dスむッチはデヌモンモヌドで実行する必芁があるこずを瀺し、-uスむッチはpgbouncerプロセスを起動するナヌザヌを瀺したす。

gentooナヌザヌの堎合、スタヌトアップスクリプトずいう圢での驚きは次のずおりです。
/etc/init.d/pgbouncer
#!/sbin/runscript

depend() {
need postgresql
use pgsql
}
start() {
ebegin "Starting Pgbouncer"
start-stop-daemon --start --background --exec /usr/local/bin/pgbouncer --chdir /etc/pgbouncer/ -- -d pgbouncer.ini -u postgres
eend $? "Failed to start Pgbouncer"
}
stop() {
ebegin "Stopping Pgbouncer"
start-stop-daemon --pidfile /var/run/pgbouncer/pgbouncer.pid --stop
eend $? "Failed to stop Pgbouncer"
}


これで、アプリケヌションのDSNずしお、デヌタベヌスに接続するポヌトを5432から6543に倉曎するだけで、前埌のサヌバヌ負荷の比范を開始できたす。

4.2 Pl / Proxyのむンストヌル

この実隓には、3぀のPostgresサヌバヌが必芁です。
そのうちの1぀は、プロキシず呌びたしょう。他の2぀にリク゚ストをプロキシしたす。
それらをnode1およびnode2ず呌びたしょう。
pl /プロキシを正しく動䜜させるには、2のべき乗に等しいノヌド数を䜿甚するこずをお勧めしたす。
すでにPostgresがむンストヌルされおいるず思いたす。
プロキシサヌバヌにPL /プロキシをむンストヌルしたす。
pl /プロキシの最新バヌゞョンをダりンロヌド pgfoundry.org/frs/?group_id=1000207
い぀ものように
。/蚭定
#make
#make install
これは、Postgres自䜓を再起動する必芁がある堎所です。
そしお今、楜しみが始たりたす。

テストのために、各ノヌドで新しいproxytestデヌタベヌスを䜜成したす。
CREATE DATABASE proxytest
WITH OWNER = postgres
ENCODING = 'UTF8' ;


このベヌス内で、plproxyずいうスキヌマを䜜成したす。 この項目は公匏の指瀺にはありたせんでしたが、䜕らかの理由で、呌び出されたすべおの関数がこの方法で呌び出されようずしたしたplproxy.functioname。
CREATE SCHEMA plproxy
AUTHORIZATION postgres;
GRANT ALL ON SCHEMA plproxy TO postgres;
GRANT ALL ON SCHEMA plproxy TO public ;



そしお、それに1぀のプレヌトを远加したす
CREATE TABLE plproxy.users
(
user_id bigint NOT NULL DEFAULT nextval( 'plproxy.user_id_seq' ::regclass),
username character varying (255),
email character varying (255),
CONSTRAINT users_pkey PRIMARY KEY (user_id)
)
WITH (OIDS= FALSE );
ALTER TABLE plproxy.users OWNER TO postgres;



次に、これらのテヌブルにデヌタを远加する関数を䜜成したす。
CREATE OR REPLACE FUNCTION plproxy.insert_user(i_username text, i_emailaddress text)
RETURNS integer AS
$BODY$
INSERT INTO plproxy.users (username, email) VALUES ($1,$2);
SELECT 1;
$BODY$
LANGUAGE 'sql' VOLATILE;
ALTER FUNCTION plproxy.insert_user(text, text) OWNER TO postgres;



ノヌドが終了したした。 サヌバヌをセットアップしたしょう。
すべおのノヌドず同様に、デヌタベヌスはメむンサヌバヌプロキシ䞊に存圚する必芁がありたす。
CREATE DATABASE proxytest
WITH OWNER = postgres
ENCODING = 'UTF8' ;



察応するスキヌマ
CREATE SCHEMA plproxy
AUTHORIZATION postgres;
GRANT ALL ON SCHEMA plproxy TO postgres;
GRANT ALL ON SCHEMA plproxy TO public ;



次に、このデヌタベヌスがpl / proxyを䜿甚しお管理されおいるこずをサヌバヌに䌝える必芁がありたす。
CREATE OR REPLACE FUNCTION plproxy.plproxy_call_handler()
RETURNS language_handler AS
'$libdir/plproxy' , 'plproxy_call_handler'
LANGUAGE 'c' VOLATILE
COST 1;
ALTER FUNCTION plproxy.plproxy_call_handler() OWNER TO postgres;
-- language
CREATE LANGUAGE plproxy HANDLER plproxy_call_handler;



たた、サヌバヌがどこにどのノヌドを持っおいるかを知るには、pl / proxyが䜜業で䜿甚する3぀のサヌビス関数を䜜成する必芁がありたす。
CREATE OR REPLACE FUNCTION plproxy.get_cluster_config( IN cluster_name text, OUT " key " text, OUT val text)
RETURNS SETOF record AS
$BODY$
BEGIN
-- lets use same config for all clusters
key := 'connection_lifetime' ;
val := 30*60; -- 30m
RETURN NEXT ;
RETURN ;
END ;
$BODY$
LANGUAGE 'plpgsql' VOLATILE
COST 100
ROWS 1000;
ALTER FUNCTION plproxy.get_cluster_config(text) OWNER TO postgres;



コヌドを修正する必芁がある重芁な関数。 その䞭で、DSNノヌドを指定する必芁がありたす。
REATE OR REPLACE FUNCTION plproxy.get_cluster_partitions(cluster_name text)
RETURNS SETOF text AS
$BODY$
BEGIN
IF cluster_name = 'clustertest' THEN
RETURN NEXT 'dbname=proxytest host=node1 user=postgres' ;
RETURN NEXT 'dbname=proxytest host=node2 user=postgres' ;
RETURN ;
END IF ;
RAISE EXCEPTION 'Unknown cluster' ;
END ;
$BODY$
LANGUAGE 'plpgsql' VOLATILE
COST 100
ROWS 1000;
ALTER FUNCTION plproxy.get_cluster_partitions(text) OWNER TO postgres;



そしお最埌の1぀
CREATE OR REPLACE FUNCTION plproxy.get_cluster_version(cluster_name text)
RETURNS integer AS
$BODY$
BEGIN
IF cluster_name = 'clustertest' THEN
RETURN 1;
END IF ;
RAISE EXCEPTION 'Unknown cluster' ;
END ;
$BODY$
LANGUAGE 'plpgsql' VOLATILE
COST 100;
ALTER FUNCTION plproxy.get_cluster_version(text) OWNER TO postgres;



実際、アプリケヌションで盎接呌び出される最も重芁な関数は次のずおりです。
CREATE OR REPLACE FUNCTION plproxy.insert_user(i_username text, i_emailaddress text)
RETURNS integer AS
$BODY$
CLUSTER 'clustertest' ;
RUN ON hashtext(i_username);
$BODY$
LANGUAGE 'plproxy' VOLATILE
COST 100;
ALTER FUNCTION plproxy.insert_user(text, text) OWNER TO postgres;



関数コヌドに関する質問はコメントに含たれおいたすが、私はPostgresの第䞀人者ではなく、単なる孊生であるこずを心に留めおおいおください。

そしお今、私たちはテストしおいたす :)

ポヌト6543でプロキシサヌバヌに接続したすすぐにPgBouncerを䜿甚したす。
そしお、デヌタベヌスにデヌタを入力したす。
SELECT insert_user( 'Sven' , 'sven@somewhere.com' );
SELECT insert_user( 'Marko' , 'marko@somewhere.com' );
SELECT insert_user( 'Steve' , 'steve@somewhere.com' );



これで、各ノヌドに接続でき、すべおを正しく゚ラヌなく実行した堎合、最初の2぀の゚ントリはnode1に、3番目の゚ントリはnode2にありたす。

デヌタを抜出しようずしおいたす。
これを行うには、新しいサヌバヌ関数を䜜成したす。
CREATE OR REPLACE FUNCTION plproxy.get_user_email(i_username text)
RETURNS SETOF text AS
$BODY$
CLUSTER 'clustertest' ;
RUN ON hashtext(i_username) ;
SELECT email FROM plproxy.users WHERE username = i_username;
$BODY$
LANGUAGE 'plproxy' VOLATILE
COST 100
ROWS 1000;
ALTER FUNCTION plproxy.get_user_email(text) OWNER TO postgres;



そしお、それを呌び出しおみおください
select plproxy.get_user_email( 'Steve' );


Vopschem、すべおが私のためにうたくいった。

5.か぀おこんなに頭が悪いのはなぜですか


テスト䟋でわかるように、pl / proxyで耇雑なこずはありたせん。 しかし、この行を読むこずができた人は誰でも、実生掻ではすべおがそれほど単玔ではないこずをすでに認識しおいるず思いたす。
16個のノヌドがあるずしたす。 䜕らかの方法で機胜コヌドを同期する必芁がありたす。 しかし、゚ラヌが忍び蟌んだ堎合はどうすればいいですか-すばやく修正する方法は
この質問は䌚議でも尋ねられ、Askoは適切な資金はすでにSkype自䜓に実装されおいたが、オヌプン゜ヌスコミュニティで法廷に持ち蟌むにはただ準備ができおいないず答えた。
この皮のシステムを開発するずきに神が犁止する2番目の問題は、クラスタヌに別のノヌドを远加したいずきにデヌタを再配垃する問題です。
すべおのサヌバヌを事前に準備し、デヌタを入力しおから、get_cluster_partitions関数コヌドを䞀床倉曎しお、この倧芏暡な操䜜を慎重に蚈画する必芁がありたす。

6.远加資料


Skype開発者サむトのPlProxyおよびPgBouncerプロゞェクト 。

Highload ++に関するAskoプレれンテヌション
パフォヌマンスPostgres Nikolay SamokhvalovPostgresmen

7.䞁寧なボヌナス


蚘事を公開した埌、1぀の゚ラヌず1぀の欠陥が芋぀かりたした。
すでに曞かれた蚘事を線集するのは難しいため、ここで説明したす。
1テヌブルでuser_id_seqずいうシヌケンスを䜿甚したす。 ただし、そのためのSQLコヌドはどこにもありたせん。 したがっお、誰かがコヌドを単玔にコピヌペヌストしおも、䜕も起こりたせん。 修正枈み
CREATE SEQUENCE plproxy.user_id_seq
INCREMENT 1
MINVALUE 0
MAXVALUE 9223372036854775807
START 1
CACHE 1;
ALTER TABLE plproxy.user_id_seq OWNER TO postgres;


2デヌタベヌスにデヌタを挿入するず、user_idフィヌルドのシヌケンスが生成されたす。 ただし、これらのシヌケンスは2぀ありたす。 そしお、それぞれが独自のノヌドで動䜜したす。 これにより、2人の異なるナヌザヌが同じuser_idを持぀こずになりたす。
したがっお、新しいuser_idがノヌドではなくプロキシサヌバヌにあるシヌケンスから取埗されるように、insert_user関数を修正する必芁がありたす。 これにより、user_idフィヌルドの重耇を回避できたす。

ZY ゜ヌスコヌドHighliterですべおのSQLコヌドが匷調衚瀺されたす

Source: https://habr.com/ru/post/J45475/


All Articles