PostgreSQL:分散データベースの一意のキー。 練習する

記事「分散データベースの一意のキー」の足跡をたどる。



共有したい基盤があります。 理想的なケースでは、マスターマスターを作りたいです。 最も難しい瞬間の1つは、すべてのサーバーでキーの一意性を確保することです。 また、もともとスケーリングを念頭に置いてベースが設計されていた場合は良いことです...繰り返しますが、これは遭遇する理想の領域からの何かであり、そうではなく、そうではありません。



そのため、マスターとマスターの同期のために準備する必要があるデータベースがあります-データベース内のすべてのキーをプロジェクト内で一意にします。



この記事ではいくつかのオプションについて言及しましたが、 Instagramによって提案されたオプションに焦点を当てます



ステップ1-すべてのキーをbigintに転送する



ここで、すべての主キーはidと呼ばれ、それに応じてこれらのキーを参照するフィールドは、order_id、client_id、table_idのように名前が付けられていることがわかります...



整数フィールドをbigintに変換する関数を作成します

DROP FUNCTION IF EXISTS "field_int2big" (field text, tablename text, table_schema text); CREATE OR REPLACE FUNCTION "field_int2big" (field text, tablename text, table_schema text) RETURNS bigint AS $body$ DECLARE BEGIN EXECUTE 'ALTER TABLE '|| table_schema || '."'|| tablename || '" ALTER COLUMN "'|| field || '" TYPE bigint;' ; return 1; END; $body$ LANGUAGE 'plpgsql';
      
      







次に、すべての整数フィールドを選択して変換します。

 select *, field_int2big(column_name, table_name, table_schema) from (select table_catalog, table_schema, table_name, column_name, data_type from information_schema.columns where table_schema in ('public', 'myscheme') and data_type in ('integer', 'oid') and (position('id' in column_name)>0 OR column_name in ('key', 'myfield')) order by table_catalog, table_schema, table_name, column_name limit 10 offset 0) c
      
      





次の点に注意してください。

  1. 独自のスキーマを追加できます/追加する必要があります: table_schema in( 'public'、 'myscheme')
  2. 「standard」という名前ではない独自のフィールドを追加することもできます: column_name in( 'key'、 'myfield')
  3. 大規模なデータベーステーブルの場合は制限10に注意してください。1に減らす必要があります-タイプの変更には時間がかかり、小さくはありません
  4. クエリは、翻訳されていない残りのフィールドを見つけるたびに、数回実行する必要があります




ステップ2-タイプを直接示す機能インデックスの翻訳



一般に、これが行われないと、将来問題が発生し、検出するのが非常に困難になります。実行可能な要求には表示されないエラーが発生します。



 DROP FUNCTION IF EXISTS "index_int2big" (idx text, declare_idx text); CREATE OR REPLACE FUNCTION "index_int2big" (idx text, declare_idx text) RETURNS text AS $body$ DECLARE new_idx text; BEGIN EXECUTE 'DROP INDEX IF EXISTS ' || idx; SELECT replace(declare_idx, 'integer', 'bigint') INTO new_idx; EXECUTE new_idx ; return new_idx; END; $body$ LANGUAGE 'plpgsql'; select *, index_int2big(indname, inddef) from (SELECT n.nspname as table_schema, c.relname as table_name, c2.relname AS indname, i.indisprimary, i.indisunique, i.indisclustered, pg_catalog.pg_get_indexdef(i.indexrelid, 0, true) AS inddef FROM pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_index i, pg_namespace n WHERE n.oid=c.relnamespace and c.oid = i.indrelid AND i.indexrelid = c2.oid and n.nspname in ('bucardo', 'public') and position('integer' in pg_catalog.pg_get_indexdef(i.indexrelid, 0, true))>0 limit 10 offset 0) c
      
      





あなたは私がブカルドサーキットでチェックインすることに気付くかもしれません。 このテクノロジーに基づく同期がすでにある場合、この手順は非常に重要になります。 また、最後のステップからのコメントを参照してください。



手順3-すべてのキーフィールドの新しいシーケンスの作成



Instagramの提案バージョンでは、スキーム/サーバーごとに一意の番号が使用されます。

つまり 各回路には、独自の一意の番号を持つ独自の機能が必要です。

関数を少し変更し、IPサーバーによって一意のキーを生成しました。



 CREATE OR REPLACE FUNCTION inet2num(inet) RETURNS numeric AS $$ DECLARE a text[] := string_to_array(host($1), '.'); BEGIN RETURN a[1]::numeric * 16777216 + a[2]::numeric * 65536 + a[3]::numeric * 256 + a[4]::numeric; END; $$ LANGUAGE plpgsql IMMUTABLE STRICT; DROP FUNCTION IF EXISTS next_id(tbl text, tableschema text); CREATE OR REPLACE FUNCTION next_id(tbl text, tableschema text = 'public') returns bigint AS $$ DECLARE our_epoch bigint := 1314220021721; seq_id bigint; now_millis bigint; shard_id bigint; result bigint; BEGIN SELECT nextval(tableschema||'."' || tbl || '_id_seq"') % 1024 INTO seq_id; /* select substring(regexp_replace(md5(current_database()||inet_server_addr()||version()), '[^\\\d]+', '', 'g')::text from 1 for 6)::int into shard_id;*/ SELECT inet2num(inet_server_addr()) into shard_id; SELECT FLOOR(EXTRACT(EPOCH FROM clock_timestamp()) * 1000) INTO now_millis; result := (now_millis - our_epoch) << 23; result := result | (shard_id << 10); result := result | (seq_id); RETURN result; END; $$ LANGUAGE PLPGSQL;
      
      







ここで、すべての識別子で呼び出す必要があります。

 DROP FUNCTION IF EXISTS "reset_nextid" (tablename text, tableschema text); CREATE OR REPLACE FUNCTION "reset_nextid" (tablename text, tableschema text) RETURNS bigint AS $body$ DECLARE id_type text; BEGIN SELECT data_type from information_schema.columns c where "table_schema"=tableschema and "table_name"=tablename and column_name='id' INTO id_type; IF id_type <> 'bigint' THEN EXECUTE 'ALTER TABLE '|| tableschema || '."'|| tablename || '" ALTER COLUMN id TYPE bigint;' ; END IF; EXECUTE 'ALTER TABLE '|| tableschema || '."'|| tablename || '" ALTER COLUMN id SET DEFAULT next_id('''|| tablename || ''', '''|| tableschema || ''');'; return next_id(tablename, tableschema); END; $body$ LANGUAGE 'plpgsql'; select t.*, reset_nextid(table_name, table_schema) from ( select t.table_schema, t.table_name, sequence_name, c.column_name from information_schema.sequences s left join information_schema.tables t on split_part(sequence_name, '_id_seq',1)=table_name left join information_schema.columns c on (t.table_schema=c.table_schema and t.table_name=c.table_name and c.column_name='id') where c.column_name is not null and position('next_id' in c.column_default)<>1 and s.sequence_schema=t.table_schema and t.table_schema in ('public', 'acc') order by t.table_schema, t.table_name limit 50 offset 0 ) as t
      
      







なぜなら 識別子のタイプは既にbigintに変更されています-リクエストはすぐに解決するはずです。



この準備は完了しました。当社の拠点は機能しており、並行して作業する準備ができています。



ボーナス


next_idで問題が発生した場合は、標準のシーケンスに戻ることができます。

 CREATE OR REPLACE FUNCTION "restore_nextval" (tablename text, tableschema text = 'public') RETURNS bigint AS $body$ DECLARE BEGIN EXECUTE 'ALTER TABLE '|| tableschema || '."'|| tablename || '" ALTER COLUMN id SET DEFAULT nextval('''|| tablename || '_id_seq''::regclass);'; return nextval((tableschema || '."'|| tablename || '_id_seq"')::regclass); END; $body$ LANGUAGE 'plpgsql'; select t.*, restore_nextval(table_name, table_schema) from ( select t.table_schema, t.table_name, sequence_name, c.column_name from information_schema.sequences s left join information_schema.tables t on split_part(sequence_name, '_id_seq',1)=table_name left join information_schema.columns c on (t.table_schema=c.table_schema and t.table_name=c.table_name and c.column_name='id') where c.column_name is not null and position('next_id' in c.column_default)>0 and s.sequence_schema=t.table_schema and t.table_schema in ('public', 'acc')
      
      







ありがとう 誰かが助けてくれたらと思います。



PS 32ビットサーバーではこれを行わないでください。 最初にサーバーをアップグレードします。 そして、アプリケーションサーバーも。



All Articles