PostgreSQLを使用したDjangoでのモデルテーブルのパーティション分割

こんにちは

これは、Django + PostgreSQLを使用する場合に、月ごとに比較的迅速かつ簡単にテーブルのパーティション化(パーティション化)を設定する方法に関するトピックです。 上記の多くは、他のフレームワークおよびORMに適しています。



パーティション化とは何か、なぜ必要なのかについては、たとえばhereherehereを読むことができます



したがって、Djangoにはプロジェクトがあり、モデルの1つのテーブルは非常に大きくなければなりません。 このテーブルからの読み取りが頻繁に発生し、レコードが作成された期間が常にわかっている場合、パーティション化によりデータベースが高速化されます。



パーティション分割を有効にするクエリを実際に作成したくない場合は、毎回自動化してみましょう。 さて、出力がSQLにあまり詳しくない人でも使用できるものである場合。 私はドキュメントを読みましたので、あなたはする必要はありません。



最初に、ベストプラクティスをすばやく試す方法を説明し、次に内部の問題について説明します。 私たちは次のように行動します:

  1. パーティション分割コマンドを追加できるように、 syncdb



    をキャッチします。
  2. 確立されたインデックスを引き出し、セクションを作成し、それらのインデックスをアクティブにし、関数とトリガーを追加するSQLを接続します。
  3. 最終アプリケーションおよびモデルでパーティション化をアクティブにします。


まず、 リポジトリからパッケージをインストールします

 pip install git+https://github.com/podshumok/django-post-syncdb-hooks
      
      





いくつかのアプリケーションを接続します。

 INSTALLED APPS = ( # ... 'post_syncdb_hooks', 'post_syncdb_hooks.partitioning', 'your.app', # ... )
      
      





yourapp/models.py



モデルがあります:

 from django.db import models class MyModel(models.Model): date_created = models.DateTimeField() my_data = models.IntegerField(db_index=True)
      
      





ファイルyourapp/sql/post_syncdb-hook.postgresql_psycopg2.sql



追加します(必要に応じて間隔を編集できます)。

 SELECT month_partition_creation( date_trunc('MONTH', NOW())::date, date_trunc('MONTH', NOW() + INTERVAL '1 year' )::date, 'yourapp_mymodel', 'date_created');
      
      





syncdb



実行しsyncdb





 ./manage.py syncdb
      
      



...およびセクションが作成されます。



これが最初のsyncdb



である場合、作成されたセクションにはインデックスがありません。 これを修正するには、 syncdb



再度実行する必要があります。



さて、ベースの準備はできましたが、Djangoはまだありません。 実際、バージョン1.3以降、DjangoはPostgreSQLのINSERT INTO



クエリを作成し、 RETURNING...



を追加してid



挿入されたレコードのid



を取得します。 また、使用しているパーティション分割方法はこの機能をサポートしていません。



大まかにDjangoでRETURNING



使用しないように強制できます。

 from django.db import connections, DEFAULT_DB_ALIAS connections[DEFAULT_DB_ALIAS].features.can_return_id_from_insert = False
      
      





そして、 RETURNING



それだけで使用されないようにモデルを編集できます:

 from post_syncdb_hooks.partitioning import to_partition class MyModel(models.Model): "..." #... @to_partition def save(self, *args, **kwargs): return super(MyModel, self).save(*args, **kwargs)
      
      





それで、ベースは準備ができています、Djangoは準備ができていますが、準備はできていますか? 読み取り要求によってすべてのセクションのデータベースポーリングが行われないように、パーティショニングが実装されているフィールド(この例ではdate_created



)でdate_created



フィルタリングする必要があります。

 qs = MyModel.objects.filter(date_created__lte=..., date_created__gt=...)
      
      





また、特別な必要なしに、すべてのエントリがどこにも発生しないcount()



ことはありません。たとえば、管理paginator



がこれを行うことを好みます。



これで全員の準備が整いました。 それだけです



post_syncdb_hooks



に関するpost_syncdb_hooks





post_syncdb_hooks



は、 post_syncdb



シグナルのレシーバーを接続するpost_syncdb_hooks



が含まれています。 このレシーバーまたはフックは、インストールされているすべてのアプリケーションに対して呼び出されます。 sql



フォルダーがこのアプリケーションまたはそのアプリケーションのmodels.py



ファイルの隣にあるかどうかを確認し、隣にある場合は、 post_syncdb-hook.sql



またはpost_syncdb-hook.(backend).sql



を起動できません。



post_syncdb_hooks.partitioning



には、このようなSQLファイルのみが含まれています。 その中で、開始日、終了日、テーブル名、フィールド名の4つのパラメーターを取るSQL関数を作成します。 関数が呼び出されると、選択されたテーブルのセクションが選択されたフィールドに作成され、開始日から終了日まで月単位で作成されます。また、 INSERT INTO



とセクションテーブルのインデックスのトリガーも作成されます(メインテーブルにインデックスが指定されている場合)。



インデックスに関するフード


私にとって最も退屈で難しいのは、セクションのインデックスを作成することでした。 テーブルを作成するためのSQLが記述されており、パーティションスキームに移行する場合、問題はありません。コピーアンドペーストです。 しかし、Djangoがテーブルを作成するとき、マスターテーブルのインデックスを作成せず、対応するSQLを保存する方法はあまり明確ではありません。 最終的に、私は決定しました:Djangoにインデックスを作成させ(それらはまだ空のままです)、セクションにコピーします。



これを行う方法を決定することは残っています。



psql



には-E



オプションがあり、内部コマンドによって生成されたSQLを出力します。 だから

 sudo -u postgres psql -E db
      
      



 db=# \di ********* QUERY ********** SELECT n.nspname as "Schema", c.relname as "Name", CASE c.relkind WHEN 'r' THEN 'table' WHEN 'v' THEN 'view' WHEN 'i' THEN 'index' WHEN 'S' THEN 'sequence' WHEN 's' THEN 'special' WHEN 'f' THEN 'foreign table' END as "Type", pg_catalog.pg_get_userbyid(c.relowner) as "Owner", c2.relname as "Table" FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace LEFT JOIN pg_catalog.pg_index i ON i.indexrelid = c.oid LEFT JOIN pg_catalog.pg_class c2 ON i.indrelid = c2.oid WHERE c.relkind IN ('i','') AND n.nspname <> 'pg_catalog' AND n.nspname <> 'information_schema' AND n.nspname !~ '^pg_toast' AND pg_catalog.pg_table_is_visible(c.oid) ORDER BY 1,2; ************************** db=#
      
      





少しコピーアンドペーストし 、テーブルテーブルのインデックスを作成するためのすべてがあります。



よろしくお願いします!


ここで紹介するパッケージは、私がいくつかのプロジェクトで使用しており、割り当てられたタスクをうまく処理しています。

これを読んでくれたみんなに感謝します。 何かが誰かに役立つことを願っています。

また、コメントとgithubで見つかった欠点に注意して 、改善と拡張を提案してください。



All Articles