SQLキヌの詳现

むンタヌネットには、リレヌショナルデヌタベヌスでキヌを遞択しお䜿甚する方法に関する独断的な教蚓がたくさんありたす。 玛争がホリバヌに倉わるこずもありたす。自然キヌたたは人工キヌを䜿甚したすか 敎数たたはUUIDを自動むンクリメントしたすか

64本の蚘事を読んで、5冊の本のセクションをめくっお、IRCずStackOverflowに぀いおたくさんの質問をした埌、私 Joe "begriffs" Nelsonによる元蚘事の著者が、パズルのピヌスを組み立おお、察戊盞手を調敎できるようになりたした。 キヌに関する倚くの玛争は、実際には、他の人の芖点の誀解のために発生したす。

内容



問題をいく぀かの郚分に分けおみたしょう。最終的には再び収集したす。 たず、質問をしたす-「キヌ」ずは䜕ですか

「キヌ」ずは䜕ですか


しばらく䞻キヌを忘れお、より䞀般的なアむデアに興味がありたす。 キヌは、行に重耇倀を持たない列です。 さらに、列は還元䞍可胜に䞀意である必芁がありたす。぀たり、列のサブセットはそれほど䞀意ではありたせん。

たずえば、カヌドゲヌムでカヌドをカりントするためのテヌブルを考えおみたしょう。

CREATE TABLE cards_seen ( suit text, face text ); 

1぀のデッキを远跡する堎合぀たり、重耇するカヌドがない堎合、シャツず顔の組み合わせは䞀意であり、同じシャツず顔を重耇しおテヌブルに远加するこずは望たしくありたせん。 カヌドがテヌブルにある堎合、私たちはそれを芋たした;さもなければ、私たちはそれを芋たせんでした。

以䞋を远加するこずにより、この制限をデヌタベヌスに蚭定できたす。

 CREATE TABLE cards_seen ( suit text, face text, UNIQUE (suit, face) ); 

それ自䜓では、 suit シャツもface 顔もナニヌクではなく、同じシャツたたは顔で異なるカヌドを芋るこずができたす。 (suit, face)䞀意であり、個々の列は䞀意ではないため、それらの組み合わせは既玄であり、 (suit, face)がキヌであるず䞻匵できたす。

より䞀般的な状況では、カヌドのいく぀かのデッキを远跡する必芁がある堎合、新しいフィヌルドを远加しお、カヌドを芋た回数を蚘録できたす。

 CREATE TABLE cards_seen ( suit text, face text, seen int ); 

トリプル(suit, face, seen)は䞀意であるこずが刀明したしたが、サブセット(suit, face)も䞀意である必芁があるため、キヌではありたせん。 これは、シャツず顔が同じで、倀が異なる2぀の行が競合する情報になるために必芁です。 したがっお、キヌは(suit, face)であり、このテヌブルにはこれ以䞊キヌはありたせん。

䞀意性の制限


PostgreSQLでは、䞀意の制玄を远加する奜たしい方法は、䟋のように盎接宣蚀するこずです。 堎合によっおは、䞀意性制玄に準拠するためにむンデックスを䜿甚する必芁がありたすが、盎接問い合わせないでください。 既に䞀意であるず宣蚀されおいる列のむンデックスを手動で䜜成する必芁はありたせん。 このようなアクションは、むンデックスの自動䜜成を単玔に耇補したす。

たた、テヌブルにはいく぀かのキヌが問題なく存圚する可胜性があり、デヌタベヌスでそれらの䞀意性を維持するためにすべおを宣蚀する必芁がありたす。

以䞋に、耇数のキヌを持぀2぀のサンプルテヌブルを瀺したす。

 --   CREATE TABLE tax_brackets ( min_income numeric(8,2), max_income numeric(8,2), tax_percent numeric(3,1), UNIQUE(min_income), UNIQUE(max_income), UNIQUE(tax_percent) ); --   CREATE TABLE flight_roster ( departure timestamptz, gate text, pilot text UNIQUE(departure, gate), UNIQUE(departure, pilot) ); 

簡朔にするために、この䟋には実甚的な他の制限はありたせん。 たずえば、カヌドのビュヌの数が負であっおはならず、NULLは怜査された列のほずんどに察しお無効ですNULLは無限を瀺すこずができる皎グルヌプのmax_income列を陀く。

䞻キヌの奇劙なケヌス


前のセクションで単に「キヌ」ず呌んだものは、通垞「候補キヌ」ず呌ばれたす。 「候補」ずいう甚語は、そのようなすべおのキヌが「プラむマリキヌ」の名誉ある圹割をめぐっお競合し、残りが「代替キヌ」代替キヌずしお割り圓おられるこずを意味したす。

SQL実装がキヌずリレヌショナルモデルの間の䞍䞀臎をなくすには時間がかかりたした。初期のデヌタベヌスは、䞻キヌの䜎レベルの抂念によっお匷化されたした。 このようなデヌタベヌスの䞻キヌは、デヌタぞの順次アクセスでメディア䞊の文字列の物理的な堎所を識別するために必芁でした。 Joe Selcoが説明する方法は次のずおりです。

「キヌ」ずいう甚語は、シリアルファむルシステムで凊理操䜜を実行するために必芁なファむル゜ヌトキヌを意味しおいたした。 パンチカヌドのセットは、たった1぀の順序で読み取られたした。 「戻る」こずは䞍可胜でした。 最初のテヌプドラむブは同じ動䜜をシミュレヌトし、双方向アクセスを蚱可したせんでした。 ぀たり、元のSybase SQL Serverが前の行を読み取るには、テヌブルを先頭に「巻き戻す」必芁がありたした。

珟代のSQLでは、情報の物理的衚珟に焊点を圓おる必芁はなく、テヌブルモデルの関係ず内郚行の順序はたったく重芁ではありたせん。 ただし、珟圚でも、SQLサヌバヌは既定で䞻キヌのクラスタヌ化むンデックスを䜜成し、叀い䌝統に埓っお、行の順序を物理的に配眮したす。

ほずんどのデヌタベヌスでは、䞻キヌは過去の遺物ずしお保持されおおり、物理的な堎所の反映たたは決定以倖にはほずんど䜕も提䟛したせん。 たずえば、PostgreSQLテヌブルでは、䞻キヌを宣蚀するず、自動的にNOT NULL制玄が課され、デフォルトの倖郚キヌが定矩されたす。 䞻キヌは、JOIN挔算子の優先列でもありたす。

䞻キヌは、他のキヌの宣蚀を排陀したせん。 同時に、プラむマリずしおキヌが割り圓おられおいない堎合、テヌブルは匕き続き正垞に機胜したす。 いずれにせよ、雷はあなたを打぀こずはありたせん。

自然キヌの怜玢



䞊蚘で説明したキヌは、誰もキヌを䜜成したくない堎合でも、モデル化されたオブゞェクトのプロパティであるため、「ナチュラル」ず呌ばれたす。

考えられる自然キヌに぀いおテヌブルを調べるずきに最初に芚えおおくべきこずは、あたりスマヌトにならないようにするこずです。 StackExchangeのsqlvogelナヌザヌは、次のアドバむスを提䟛したす。

䞀郚の人々は、特定のキヌが䞀意ではないずいう仮想的な状況に陥るため、「自然な」キヌを遞択するのが困難です。 圌らはタスクの意味そのものを理解しおいたせん。 キヌの意味は、特定のテヌブルで属性が垞に䞀意であり、垞に䞀意であるルヌルを決定するこずです。 テヌブルには、特定の十分に理解されたコンテキスト「䞻題領域」たたは「談話領域」のデヌタが含たれ、唯䞀の倀はこの特定の領域での制限の適甚です。

緎習は、列が既存の倀で䞀意であり、可胜性のあるシナリオではそうである堎合、キヌ制玄を入力する必芁があるこずを瀺しおいたす。 たた、必芁に応じお、制限を削陀できたすこれが気になる堎合は、以䞋にキヌの安定性に぀いお説明したす。

たずえば、趣味クラブのメンバヌのデヌタベヌスは、first_name、last_nameの2぀の列に䞀意性を持っおいる堎合がありたす。 少量のデヌタでは、重耇はほずんどありたせん。実際の競合が発生する前に、そのようなキヌを䜿甚するこずは非垞に合理的です。

デヌタベヌスの成長ず情報量の増加により、自然キヌの遞択はより困難になる可胜性がありたす。 栌玍するデヌタは倖郚の珟実を単玔化したものであり、座暙が時間ずずもに倉化するなど、䞖界のオブゞェクトを区別するいく぀かの偎面は含たれおいたせん。 オブゞェクトにコヌドがない堎合、空間内の䜍眮たたは重量たたはパッケヌゞのわずかな違いを陀いお、2猶の飲料たたは2箱のオヌトミヌルを区別する方法は

それが、暙準化団䜓が補品に独特のラベルを䜜成しお貌る理由です。 車には車䞡識別番号VINが刻印され、ISBNは本に印刷され、UPCは食品の包装に刻印されおいたす。 これらの数倀は自然なものではないず䞻匵するかもしれたせん。 では、なぜそれらを自然キヌず呌ぶのですか

デヌタベヌス内の䞀意のプロパティの自然性たたは人工性は、倖郚の䞖界に関連しおいたす。 暙準化団䜓たたは囜家機関で人工的に䜜成されたキヌは、䞖界䞭で暙準になり、オブゞェクトに印刷されるため、私たちにずっお自然になりたす。
通貚、蚀語、金融商品、化孊薬品、医療蚺断など、さたざたな斜蚭には倚くの業界、瀟䌚、および囜際芏栌がありたす。 以䞋は、自然キヌずしおよく䜿甚される倀の䞀郚です。


可胜か぀劥圓な堎合はキヌを宣蚀するこずをお勧めしたす。テヌブルごずにいく぀かのキヌを宣蚀するこずもできたす。 ただし、䞊蚘のすべおに䟋倖がある堎合があるこずに泚意しおください。



人工キヌ



キヌが各行に䞀意の倀を持぀列である堎合、それを䜜成する方法の1぀はチヌトです-各行に独自の䞀意の倀を曞き蟌むこずができたす。 これらは人工的なキヌです。デヌタたたはオブゞェクトを参照するために䜿甚される発明されたコヌド。

コヌドがデヌタベヌス自䜓から生成され、デヌタベヌスナヌザヌ以倖の誰にも知られないこずが非垞に重芁です。 これが、人工キヌず暙準化された自然キヌを区別するものです。

自然キヌの利点は、テヌブル行の重耇たたは䞍敎合から保護するこずです。䞀方、人為的なキヌは、文字列たたは耇数列比范を䜿甚しないため、人や他のシステムが行を参照しやすくなり、怜玢および結合操䜜の速床が向䞊するため䟿利ですキヌ。

代理


人工キヌはバむンディングずしお䜿甚されたす-ルヌルず列の倉曎に関係なく、1行は垞に同じ方法で識別できたす。 この目的で䜿甚される人工キヌは「代理キヌ」ず呌ばれ、特別な泚意が必芁です。 以䞋で怜蚎するサロゲヌト。
非代理人工キヌは、デヌタベヌス倖の行を参照するのに圹立ちたす。 人工キヌは、デヌタたたはオブゞェクトを簡単に識別したす。URLずしお指定したり、アカりントに添付したり、電話で口述したり、銀行で受け取ったり、ナンバヌプレヌトに印刷したりできたす。 私たちの車のナンバヌプレヌトは自然なキヌですが、それは囜家によっお人工キヌずしお蚭蚈されおいたす。

入力ミスや゚ラヌを最小限に抑えるために、考えられる送信方法を考慮しお、人工キヌを遞択する必芁がありたす。 キヌの発音、印刷、SMSによる送信、手曞きの読み取り、キヌボヌドからの入力、URLぞの埋め蟌みが可胜です。 さらに、クレゞットカヌド番号などの䞀郚の人工キヌにはチェックサムが含たれおいるため、特定の゚ラヌが発生したずきに少なくずも゚ラヌを認識できたす。

䟋


人工キヌに䞖界を玹介するずすぐに、人々は奇劙な方法で特別な泚意を払い始めるこずに泚意しおください。 「泥棒」のナンバヌプレヌトたたは発音可胜な識別子を䜜成するためのシステムを芋おください。これは悪名高いcursesの自動生成プログラムになりたした 。

数字キヌに限定しおも、 13階のようなタブヌがありたす。 プロクむントは発音された音節に関する情報の密床が高いずいう事実にもかかわらず、数字も倚くの堎合に適しおいたすURL、ピンキヌボヌド、手曞き入力では、キヌが数字のみで構成されおいるこずを受信者が知っおいる堎合。
ただし、リ゜ヌス /videos/1.mpegなどを/videos/2.mpegたり、番号に関する情報の挏えいを䜜成したりできるため、公的にアクセス可胜な数字キヌで順番を䜿甚しないでください。デヌタ。 Feistelネットワヌクを䞀連の数字でオヌバヌレむし、䞀意性を保持しながら、数字の順序を隠したす。
PostgreSQL wikiには、 擬䌌暗号化関数の䟋がありたす。

 CREATE OR REPLACE FUNCTION pseudo_encrypt(VALUE int) returns int AS $$ DECLARE l1 int; l2 int; r1 int; r2 int; i int:=0; BEGIN l1:= (VALUE >> 16) & 65535; r1:= VALUE & 65535; WHILE i < 3 LOOP l2 := r1; r2 := l1 # ((((1366 * r1 + 150889) % 714025) / 714025.0) * 32767)::int; l1 := l2; r1 := r2; i := i + 1; END LOOP; RETURN ((r1 << 16) + l1); END; $$ LANGUAGE plpgsql strict immutable; 

この関数はそれ自䜓ずは逆です぀たり、 pseudo_encrypt(pseudo_encrypt(x)) = x 。 関数の正確な再珟は、あいたいさによる䞀皮のセキュリティであり、PostgreSQLのドキュメントからFeistelネットワヌクを䜿甚しおいるこずに気付いた人は、元のシヌケンスを簡単に取埗できたす。 ただし、 (((1366 * r1 + 150889) % 714025) / 714025.0)代わりに、0から1の倀の範囲で別の関数を䜿甚できたす。たずえば、前の匏の数倀を詊しおください。

pseudo_encryptの䜿甚方法は次のずおりです。

 CREATE SEQUENCE my_table_seq; CREATE TABLE my_table ( short_id int NOT NULL DEFAULT pseudo_encrypt( nextval('my_table_seq')::int ), --   
 UNIQUE (short_id) ); 

この゜リュヌションは、 short_id列にランダムな倀を保存したすが、高いデヌタ凊理速床を維持するこずが重芁な堎合は、増分シヌケンス自䜓をテヌブルに保存し、 pseudo_encrypt衚瀺するずきに倉換pseudo_encryptたす。 埌で芋るように、ランダム化された倀にむンデックスを付けるず、録音ボリュヌムが増加する可胜性がありたす。

前の䟋では、通垞サむズの敎数倀がbigintに䜿甚されたしたbigint 、 XTEAなどの他のFeistel関数がありたす。

敎数のシヌケンスを混同する別の方法は、それを短い文字列に倉換するこずです。 pg_hashids拡匵機胜を䜿甚しおみおください。

 CREATE EXTENSION pg_hashids; CREATE SEQUENCE my_table_seq; CREATE TABLE my_table ( short_id text NOT NULL DEFAULT id_encode( nextval('my_table_seq'), ' long string as table-specific salt ' ), --   
 UNIQUE (short_id) ); INSERT INTO my_table VALUES (DEFAULT), (DEFAULT), (DEFAULT); SELECT * FROM my_table; /* ┌──────────┐ │ short_id │ ├─────────── │ R4 │ │ ya │ │ Ll │ └──────────┘ */ 

ここでも、敎数そのものをテヌブルに栌玍し、必芁に応じお倉換する方が高速ですが、パフォヌマンスを枬定し、本圓に意味があるかどうかを確認したす。

さお、人工キヌず自然キヌの意味を明確に区別するず、「自然察人工」論争は誀った二分法であるこずがわかりたす。 人工キヌず自然キヌはお互いを排陀したせん 1぀のテヌブルに䞡方がある堎合がありたす。 実際、人工キヌのあるテヌブルは自然キヌも提䟛する必芁がありたすが、自然キヌがない堎合はたれに䟋倖がありたすたずえば、クヌポンコヌドコヌドのテヌブル内。

 --   :    , --        "code" CREATE TABLE coupons ( code text NOT NULL, amount numeric(5,2) NOT NULL, redeemed boolean NOT NULL DEFAULT false, UNIQUE (code) ); 

人工キヌがあり、自然キヌが存圚するずきに宣蚀しない堎合、埌者を保護しないでください。

 CREATE TABLE cars ( car_id bigserial NOT NULL, vin varchar(17) NOT NULL, year int NOT NULL, UNIQUE (car_id) --    -- UNIQUE (vin) ); --  ,    INSERT INTO cars (vin, year) VALUES ('1FTJW36F2TEA03179', 1996), ('1FTJW36F2TEA03179', 1997); 

远加のキヌを宣蚀するこずに察する唯䞀の議論は、それぞれの新しいキヌが別の䞀意のむンデックスを保持し、テヌブルぞの曞き蟌みコストを増加させるこずです。 もちろん、それはあなたにずっおデヌタの正確さがどれほど重芁かによっお異なりたすが、ほずんどの堎合、キヌはただ宣蚀する䟡倀がありたす。

たた、もしあれば、いく぀かの人工的なキヌを宣蚀する䟡倀がありたす。 たずえば、組織には応募者ず埓業員がいたす。 各埓業員はか぀お候補者でしたが、埓業員のキヌでもある独自の識別子で候補者を参照したす。 別の䟋ずしお、埓業員IDずログむン名をEmployeesの2぀のキヌずしお蚭定できたす。

代理キヌ



すでに述べたように、重芁なタむプの人工キヌは「代理キヌ」ず呌ばれたす。 他の人工キヌのように簡朔で転送可胜である必芁はありたせんが、垞に文字列を識別する内郚ラベルずしお䜿甚されたす。 SQLで䜿甚されたすが、アプリケヌションは明瀺的にアクセスしたせん。

PostgreSQLのシステムカラムシステムカラムに粟通しおいる堎合、サロゲヌトはほずんどデヌタベヌス実装パラメヌタヌctidなどであるず考えるこずができたすが、倉曎はありたせん。 サロゲヌト倀は行ごずに1回遞択され、その埌は倉曎されたせん。

代理キヌは倖郚キヌずしお優れおおり、カスケヌドのON UPDATE RESTRICT制玄は、代理の䞍倉性ず䞀臎するように指定する必芁がありたす。

䞀方、公的に送信されるキヌに察する倖郚キヌは、最倧限の柔軟性を提䟛するためにON UPDATE CASCADEずマヌクする必芁がありたす。 カスケヌド曎新は、それを取り巻くトランザクションず同じ分離レベルで実行されるため、同時アクセスの問題を心配する必芁はありたせん。厳密な分離レベルを遞択するず、デヌタベヌスが察凊したす。

代理キヌを「自然」にしないでください。 , , , ( ), . - .

, , , .

bigint


«bigserial», IDENTITY . ( , PostgreSQL 10 , Oracle, IDENTITY, . CREATE TABLE .)

, , . , .

:



UUID


: (128-), . (universally unique identifier, UUID) , .

, UUID , ? , !

PostgreSQL? , , , .

, . , UUID — , : 5bd68e64-ff52-4f54-ace4-3cd9161c8b7f. , (128-) uuid, PostgreSQL bigint, .., , .

UUID , , ? , , ( ) UUID. , UUID , SQL psql , . , .

UUID , (write amplification) - (write-ahead log, WAL). , UUID.

write amplification. , . PostgreSQL , «» . , . PostgreSQL , .

PostgreSQL / / , (write-ahead log), . UUID ( 4 8 ) WAL . (full-page write, FPW).

UUID (, «snowflake» Twitter uuid_generate_v1() uuid-ossp PostgreSQL) . FPW.

FPW UUID, WAL. .


:

 CREATE EXTENSION "uuid-ossp"; CREATE EXTENSION pgcrypto; CREATE TABLE u_v1 ( u uuid PRIMARY KEY ); CREATE TABLE u_crypto ( u uuid PRIMARY KEY ); 

, UUID , write-ahead log.

 SELECT pg_walfile_name(pg_current_wal_lsn()); /* , pg_walfile_name -------------------------- 000000010000000000000001 */ 

, WAL . , :

 pg_waldump --stats 000000010000000000000001 

:

  1. UUID, gen_random_uuid() ( pgcrypto )
  2. uuid_generate_v1() ( [uuid-ossp] (https://www.postgresql.org/docs/10/static/uuid-ossp.html)
  3. gen_random_uuid() , full_page_writes='off' . , FPW.

2 20 UUID , , , .

 -- ,     psql 16    \timing INSERT INTO u_crypto ( SELECT gen_random_uuid() FROM generate_series(1, 1024*1024) ); 

:


UUID

WAL :

 gen_random_uuid()

 N (%)   (%)  FPI (%)
---- - --- ----------- --- -------- ---
XLOG 260 ( 0.15) 13139 ( 0.09) 484420 ( 30.94)
Heap2 765 ( 0.45) 265926 ( 1.77) 376832 ( 24.07)
Heap 79423 ( 46.55) 6657121 ( 44.20) 299776 ( 19.14)
Btree 89354 ( 52.37) 7959710 ( 52.85) 404832 ( 25.85)

uuid_generate_v1()

 N (%)   (%)  FPI (%)
---- - --- ----------- --- -------- ---
XLOG 0 ( 0.00) 0 ( 0.00) 0 ( 0.00)
Heap2 0 ( 0.00) 0 ( 0.00) 0 ( 0.00)
Heap 104326 ( 49.88) 7407146 ( 44.56) 0 ( 0.00)
Btree 104816 ( 50.12) 9215394 ( 55.44) 0 ( 0.00)

gen_random_uuid() with fpw=off

 N (%)   (%)  FPI (%)
---- - --- ----------- --- -------- ---
XLOG 4 ( 0.00) 291 ( 0.00) 64 ( 0.84)
Heap2 0 ( 0.00) 0 ( 0.00) 0 ( 0.00)
Heap 107778 ( 49.88) 7654268 ( 46.08) 0 ( 0.00)
Btree 108260 ( 50.11) 8956097 ( 53.91) 7556 ( 99.16) 

, gen_random_uuid WAL - (full-page images, FPI), . , . FPW , , . , ZFS FPW, .

uuid_generate_v1() – . uuid-ossp , RDS Citus Cloud, .

uuid_generate_v1:

MAC- . , UUID , , , , .


, , . - , uuid_generate_v1mc() , mac- .


, , .

:

  1. .
  2. <table_name>_id uuid uuid_generate_v1() . . , JOIN, .. JOIN foo USING (bar_id) JOIN foo ON (foo.bar_id = bar.id) . .
  3. , JOIN, .
  4. , URL . pg_hashids, .
  5. ON UPDATE RESTRICT , UUID , – ON UPDATE CASCADE . , .


, . , - . , « » .



. -, , , - , ++ , 8 , DevOps . .

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


All Articles