すでにご存知のように、 Avitoのすべての広告はPostgreSQL に存在します。 このデータベースの機能は、データレベルだけでなく、ストアドプロシージャ、トリガー、関数を通じてこのデータへのアクセスを提供する独自のAPIの作成にも基づいて、非常に優れた機能を提供します。 この構造全体で作業する場合、いくつかの変更が必要になることがよくあります。 そして最も単純なケースでは、開発者が1つのクライアントと1つのデータベースを扱う場合、更新プロセスは非常に単純に見えます。変更、移行スクリプト、そしてそれだけです。 しかし、そのような状況はまれであり、多くの場合、製品の顧客とデータベースは数百になります。 したがって、通常のデータベースライフサイクルでは、コードをバージョン管理するメカニズムが不可欠です。
この記事では、Avitoで実装したデータベース内のコードをバージョン管理する2つの方法について説明します。 この情報が役立つ場合があります。 まず、少しのコンテキスト。
Avitoは:
- 膨大な数のサーバーとさらに多くのデータベース。
- すべてのベースの合計サイズは15 Tbです。
- 非常に高いTPS、平均10K。
- 多くの開発者とgitブランチ。
最初のタスク:
- 異なるgitブランチの下で同じベースに複数のバージョンのプロシージャを展開します。
- ストアドプロシージャコードの便利なバージョン管理。
最初のバージョン管理オプション
私たちが思いついた最初のオプションは、辞書によるバージョン管理です。
詳細
- PHPコードから呼び出されるストアドプロシージャのみがプロジェクトに格納され、デプロイの対象となります。
- プロジェクト内にファイル表現を持たないストアドプロシージャは、移行ツールやDBAコマンドを使用してデプロイされます。
- デプロイされる各データベースには、stored_proceduresテーブルがあります。
列名 | 説明 | 例 |
枝 | 役職
| ロケーションID修正 |
fn_name | 役職
| core.location_save |
fn_md5 | ハッシュ量
| 0539f31fee4efd845a24c9878cd721b2 |
ver_id | バージョン番号
| 2 |
create_txtime | 時間
| 2016-12-11 10:16:10 |
update_txtime | 時間
| 2016-12-11 11:23:14 |
- このプロジェクトには、ブランチごとにテーブルからフィルタリングされたデータを含むphp辞書があります(ブランチ= '<現在のブランチの名前>')。 その結果、辞書にはこのブランチのすべてのストアドプロシージャの名前(データベースの名前を含む)とver_idが含まれます。
1 => array ( 'verId' => 2, 'hash' => '0539f31fee4efd845a24c9878cd721b2', 'fnFullName' => 'core.location_save@master' )
- ストアドプロシージャのバージョンは、名前のポストフィックスから決定されます。このポストフィックスの形式は、 <ストアドプロシージャ名> _ver#で 、 #はバージョン番号です。
- stored_proceduresのブランチ列のおかげで、異なるブランチは同じ名前のストアドプロシージャを呼び出すことができます。これらのストアドプロシージャは異なるコードを持ち、したがってバージョンも異なります。
- ブランチでの開発が完了すると、ストアドプロシージャのコード(phpコードなど)がマスターに固定されます。
- ストアドプロシージャのファイル名にバージョン(core.location_save_ver2.sqlではなくcore.location_save.sql)が含まれていないため、ブランチの各ストアドプロシージャで行われた変更は、プロセス中に1行ずつ表示されます。
- PHPコードでは、ストアドプロシージャはプレースホルダーバージョンを通じて呼び出されます。
$this->db->exec( "select core.location_save%ver%(...)" );
- 呼び出されると、プレースホルダーは_verプレフィックス付きのバージョン番号に置き換えられます。たとえば、バージョン2の場合、%ver%の代わりに_ver2が使用されます。
ストアドプロシージャの展開は、辞書のアセンブリの前に、プロジェクトアセンブリの最初のステップで実行されます。
プロジェクトの各ストアドプロシージャファイルについて:
- ファイルの内容からハッシュ合計が計算され、ストアドプロシージャの最小バージョンがstored_proceduresテーブルの新しいハッシュ合計で検索されます。
- 何も見つからなかった場合(以前はこのようなプロシージャはどのブランチにもデプロイされていませんでした)、新しいプロシージャのバージョンがインクリメントされ、データベースへのこのプロシージャのデプロイが許可されます。
- この新しいハッシュを使用するストアドプロシージャが他のブランチで既に使用されている場合、現在のブランチは、データベースへの新しいデプロイメントなしで、このプロシージャを最小バージョンでも使用します。
- このブランチでこのストアドプロシージャが以前に使用され、新しいハッシュが現在のレコードのstored_proceduresテーブルのハッシュと異なり、新しいハッシュを含むこのストアドプロシージャが...
-他のブランチで使用されておらず、最小バージョンが不明な場合、新しい手順ではバージョンが増加し、データベースへの展開が許可されます。
-他のブランチで使用されていて、最小バージョンがわかっている場合、現在のブランチは、データベースへの新しいコード展開なしで、最小バージョンの既存のストアドプロシージャを使用します。 - stored_proceduresテーブルへの初期登録またはver_id更新の場合、ストアドプロシージャを作成するためのコードは、ストアドプロシージャを作成するためのSQLヘッダーに事前に準備されたバージョンを使用してターゲットベースで実行されます。
CREATE OR REPLACE FUNCTION core.location_save(...)
PHPではになります
CREATE OR REPLACE FUNCTION core.location_save_ver2(...)
ベースで実行します。
core.location_save.sqlファイルは変更されません。
- 次に、ディクショナリがアセンブルされます。このステージには、このブランチのストアドプロシージャの現在のバージョンが含まれています。
このバージョン管理コードの利点:
- 変更されたストアドプロシージャのみが展開されます。
- ストアドプロシージャの複数のバージョンを1つのデータベースに保存することができます。
- 簡単な「ロールバック」。
短所:
- 内部ストアドプロシージャの展開と使用に関する問題(1つのストアドプロシージャを別のストアドプロシージャから呼び出す)。
- ストアドプロシージャの古いバージョンをクリーンアップするにはツールが必要です。
- データベースが作成されるバージョンに関する情報は一元化されていません(データベースの2番目のコピー(書き込み可能)をデプロイメントに接続する場合、一般的なデプロイメントロックの下でstored_proceduresテーブルを同期する必要があります)。
2番目のバージョン管理オプション
あなたが推測した次のオプションは、前のオプションのマイナスから来ました。 新しいプロジェクトアセンブリごとに一意のスキームとユーザーを作成することにより 、 バージョン管理として指定します。
詳細
すべてのアセンブリに関する情報は、メインサーバー上のデータベースのbuild_historyテーブルに格納されます。
列名 | 説明 | 例 |
build_branch | 収集するブランチの名前 | deploy_search_path |
build_tag | プロジェクトの将来のアーカイブの名前 | デプロイ_1501247988 |
build_time | プロジェクトのビルド時間 | 17/28/17 1:19:48午後 |
schema_name | プロジェクトの指定スキーム | z_build_1 |
schema_user | プロジェクトの指定データベースユーザー | user_1 |
deploy_time | 新しいプロジェクトコードに切り替える時間 | 07/28/17 14:05:22 |
- 新しいプロジェクトアセンブリごとに、ブランチのコンテキストで、データベースに一意のスキームが作成されます。
- テストアセンブリの場合、回路の形式はz_build_test_Nで、 Nは循環シーケンス( 1〜n1 )です。
- テストビルドの場合、ユーザーの形式はuser_test_Nです 。ここで、 Nは循環シーケンス( 1〜n1 )です。
- 戦闘アセンブリの場合、回路の形式はz_build_Nです 。ここで、 Nは循環シーケンス( 1〜n2 )です。
- コンバットビルドの場合、ユーザーの形式はuser_Nで、 Nは循環シーケンス(1からn2)です。
- 各スキームには、データベースサーバーに接続するための独自の一意のユーザーがいます。
- すべてのストアドプロシージャが展開されます。
- スキームは周期的に再作成されます。
- PHPコードでは、スキームとプレースホルダーバージョン%ver%を指定せずにストアドプロシージャが呼び出されます。
プロジェクトの組み立て中の戦闘展開のプロセス:
- アセンブリが起動されると、新しいアセンブリに関する情報がbuild_historyテーブルに記録され、データベースサーバーに接続するための一意のスキームとユーザーが割り当てられます。
- ユーザーは、プロジェクトコードと共に展開される構成に書き込みます。
- デプロイメント用の特別なユーザーの下にデータベースサーバーへの接続があります。
- データベースでは、ストアドプロシージャが割り当てられたスキーマが作成されます(存在する場合は再作成されます)。
- プロジェクトコードがすべてのアプリケーションサーバーに配置された後、シンボリックリンクが新しいプロジェクトコードに置き換えられると、これらのサーバーのいずれかがメインサーバーにアクセスします。
-build_historyテーブルで新しいプロジェクトコードに切り替える時間を設定します。
- プロダクショングループは選択したユーザーに割り当てられ、誰が戦闘に参加しているかを知るために、さらにシンボリックリンクを切り替えずにプロジェクトを繰り返し再構築する場合に、ストアドプロシージャでスキームを誤ってやり過ぎないようにします。
-スキームが作成されたすべてのサーバーで、フォームの新しいsearch_pathが設定されます。
search_path = public、<assigned schema>の場合:
-専用ユーザーuser_N;
-DBA開発者およびチーム。
-さまざまなクラウンなどのユーザー
pgbouncerでプールを設定するための重要な追加
pgbouncerを使用する場合、プールのサイズを制限するには、 pool_sizeと等しいmax_db_connectionsオプションを使用する必要があります。 これがない場合、各プールユーザーは独自のpool_sizeを持ちます。 この動作は文書化されていませんが、max_db_connectionsは次のように機能します。プール内のすべてのユーザーのアクティブなトランザクションの数を制限します。
pgbouncerプールの例:
my_database = host=localhost pool_size=5 max_db_connections=5
結論として、提示されたコードバージョン管理のバージョンは、24時間365日の高負荷モードで優れた結果を示し、ハイブリッドモードで使用されていることに注意してください。 しかし最近、search_pathの2番目のメソッドをより優先します。
ご清聴ありがとうございました!