PostgreSQL
 sql >> डेटाबेस >  >> RDS >> PostgreSQL

PostgreSQL तार्किक प्रतिकृति Gotchas

PostgreSQL 10 तार्किक प्रतिकृति के स्वागत योग्य जोड़ के साथ आया है विशेषता। यह नियमित स्ट्रीमिंग प्रतिकृति तंत्र की तुलना में आपके टेबल को दोहराने के लिए एक अधिक लचीला और आसान साधन प्रदान करता है। हालाँकि, इसकी कुछ सीमाएँ हैं जो आपको इसे प्रतिकृति के लिए नियोजित करने से रोक भी सकती हैं और नहीं भी। अधिक जानने के लिए पढ़ें।

वैसे भी तार्किक प्रतिकृति क्या है?

स्ट्रीमिंग प्रतिकृति

V10 से पहले, सर्वर में रहने वाले डेटा को दोहराने का एकमात्र तरीका WAL-स्तर पर परिवर्तनों को दोहराना था। इसके संचालन के दौरान, एक PostgreSQL सर्वर(प्राथमिक ) वाल फाइलों का एक क्रम उत्पन्न करता है। मूल विचार इन फ़ाइलों को किसी अन्य PostgreSQL सर्वर (स्टैंडबाय . पर ले जाना है ) जो इन फाइलों को लेता है और प्राथमिक सर्वर पर हो रहे समान परिवर्तनों को फिर से बनाने के लिए उन्हें "रिप्ले" करता है। स्टैंडबाय सर्वर एक रीड ओनली मोड में रहता है जिसे पुनर्प्राप्ति मोड . कहा जाता है , और स्टैंडबाय सर्वर में कोई भी परिवर्तन नहीं हैं अनुमति है (अर्थात, केवल-पढ़ने के लिए लेनदेन की अनुमति है)।

WAL फ़ाइलों को प्राथमिक से स्टैंडबाय में भेजने की प्रक्रिया को लॉगशीपिंग . कहा जाता है , और मैन्युअल रूप से किया जा सकता है (प्राथमिक के $PGDATA/pg_wal से rsync परिवर्तन की स्क्रिप्ट निर्देशिका से माध्यमिक) या स्ट्रीमिंग प्रतिकृति . के माध्यम से .विभिन्न सुविधाएं जैसे प्रतिकृति स्लॉट , अतिरिक्त फ़ीडबैक और विफलता स्ट्रीमिंग प्रतिकृति की विश्वसनीयता और उपयोगिता में सुधार के लिए समय के साथ जोड़े गए थे।

स्ट्रीमिंग प्रतिकृति की एक बड़ी "विशेषता" यह है कि यह सब कुछ या कुछ भी नहीं है। प्राथमिक पर सभी डेटाबेस से सभी ऑब्जेक्ट्स में सभी परिवर्तनों को स्टैंडबाय पर भेजना होगा, और स्टैंडबाय को प्रत्येक परिवर्तन को आयात करना होगा। आपके डेटाबेस के किसी भाग को चुनिंदा रूप से दोहराना संभव नहीं है।

तार्किक प्रतिकृति

तार्किक प्रतिकृति , v10 में जोड़ा गया, ऐसा करना संभव बनाता है - अन्य सर्वरों के लिए केवल तालिकाओं का एक सेट। इसे एक उदाहरण से सबसे अच्छी तरह समझाया गया है। आइए एक डेटाबेस लेते हैं जिसे src . कहा जाता है एक सर्वर में, और उसमें एक टेबल बनाएं:

src=> CREATE TABLE t (col1 int, col2 int);
CREATE TABLE
src=> INSERT INTO t VALUES (1,10), (2,20), (3,30);
INSERT 0 3

हम एक प्रकाशन भी बनाने जा रहे हैं इस डेटाबेस में (ध्यान दें कि ऐसा करने के लिए आपके पास सुपरयुसर विशेषाधिकार होने चाहिए):

src=# CREATE PUBLICATION mypub FOR ALL TABLES;
CREATE PUBLICATION

अब चलिए एक डेटाबेस dst पर चलते हैं किसी अन्य सर्वर पर और एक समान तालिका बनाएं:

dst=# CREATE TABLE t (col1 int, col2 int, col3 text NOT NULL DEFAULT 'foo');
CREATE TABLE

और अब हम एक सदस्यता . सेट करते हैं यहां जो स्रोत पर प्रकाशन से जुड़ जाएगा और परिवर्तनों में खींचना शुरू कर देगा। (ध्यान दें कि आपके पास एक उपयोगकर्ता होना चाहिएrepuser स्रोत सर्वर पर प्रतिकृति विशेषाधिकारों के साथ और तालिकाओं तक पढ़ने की पहुंच।)

dst=# CREATE SUBSCRIPTION mysub CONNECTION 'user=repuser password=reppass host=127.0.0.1 port=5432 dbname=src' PUBLICATION mypub;
NOTICE:  created replication slot "mysub" on publisher
CREATE SUBSCRIPTION

परिवर्तन समन्वयित हैं, और आप पंक्तियों को गंतव्य की ओर देख सकते हैं:

dst=# SELECT * FROM t;
 col1 | col2 | col3
------+------+------
    1 |   10 | foo
    2 |   20 | foo
    3 |   30 | foo
(3 rows)

गंतव्य तालिका में एक अतिरिक्त कॉलम "col3" है, जो कि प्रतिकृति द्वारा छुआ नहीं गया है। परिवर्तनों को "तार्किक रूप से" दोहराया जाता है - इसलिए, जब तक केवल t.col1 और t.col2 के साथ एक पंक्ति सम्मिलित करना संभव है, प्रतिकृति प्रक्रिया समान होगी।

स्ट्रीमिंग प्रतिकृति की तुलना में, तार्किक प्रतिकृति सुविधा किसी विशिष्ट डेटाबेस में किसी एकल स्कीमा या तालिकाओं के एक सेट को दूसरे सर्वर पर दोहराने के लिए एकदम सही है।

स्कीमा परिवर्तनों की प्रतिकृति

मान लें कि आपके पास स्रोत डेटाबेस में रहने वाली तालिकाओं के सेट के साथ एक Django एप्लिकेशन है। इन सभी तालिकाओं को किसी अन्य सर्वर में लाने के लिए तार्किक प्रतिकृति सेटअप करना आसान और कुशल है, जहां आप "वास्तविक" डेटा को छुए बिना और उत्पादन ऐप को प्रभावित किए बिना रिपोर्टिंग, एनालिटिक्स, बैच जॉब, डेवलपर / ग्राहक सहायता ऐप और इसी तरह चला सकते हैं।

संभवतः वर्तमान में तार्किक प्रतिकृति की सबसे बड़ी सीमा यह है कि यह स्कीमा परिवर्तनों को दोहराता नहीं है - स्रोत डेटाबेस पर निष्पादित कोई भी डीडीएल कमांड स्ट्रीमिंग प्रतिकृति के विपरीत, गंतव्य डेटाबेस में समान परिवर्तन का कारण नहीं बनता है। उदाहरण के लिए, यदि हम इसे स्रोत डेटाबेस में करते हैं:

src=# ALTER TABLE t ADD newcol int;
ALTER TABLE
src=# INSERT INTO t VALUES (-1, -10, -100);
INSERT 0 1

यह गंतव्य लॉग फ़ाइल में लॉग इन हो जाता है:

ERROR:  logical replication target relation "public.t" is missing some replicated columns

और प्रतिकृति बंद हो जाती है। कॉलम को गंतव्य पर "मैन्युअल रूप से" जोड़ा जाना है, जिस बिंदु पर प्रतिकृति फिर से शुरू होती है:

dst=# SELECT * FROM t;
 col1 | col2 | col3
------+------+------
    1 |   10 | foo
    2 |   20 | foo
    3 |   30 | foo
(3 rows)

dst=# ALTER TABLE t ADD newcol int;
ALTER TABLE
dst=# SELECT * FROM t;
 col1 | col2 | col3 | newcol
------+------+------+--------
    1 |   10 | foo  |
    2 |   20 | foo  |
    3 |   30 | foo  |
   -1 |  -10 | foo  |   -100
(4 rows)

इसका मतलब यह है कि यदि आपके Django एप्लिकेशन ने एक नई सुविधा जोड़ी है जिसके लिए नए कॉलम या टेबल की आवश्यकता है, और आपको django-admin migrate चलाना होगा स्रोतडेटाबेस पर, प्रतिकृति सेटअप टूट जाता है।

समाधान

इस समस्या को ठीक करने के लिए आपकी सबसे अच्छी शर्त यह होगी कि आप गंतव्य पर सदस्यता को रोकें, पहले गंतव्य को माइग्रेट करें, फिर स्रोत और फिर सदस्यता को फिर से शुरू करें। आप इस तरह सदस्यता रोक सकते हैं और फिर से शुरू कर सकते हैं:

-- pause replication (destination side)
ALTER SUBSCRIPTION mysub DISABLE;

-- resume replication
ALTER SUBSCRIPTION mysub ENABLE;

यदि नई तालिकाएँ जोड़ी जाती हैं और आपका प्रकाशन “सभी तालिकाओं के लिए” नहीं है, तो आपको उन्हें मैन्युअल रूप से प्रकाशन में जोड़ना होगा:

ALTER PUBLICATION mypub ADD TABLE newly_added_table;

पोस्टग्रेज़ को नई तालिकाएँ सिंक करना शुरू करने के लिए कहने के लिए आपको गंतव्य पक्ष पर सदस्यता को "रीफ्रेश" करने की भी आवश्यकता होगी:

dst=# ALTER SUBSCRIPTION mysub REFRESH PUBLICATION;
ALTER SUBSCRIPTION

अनुक्रम

स्रोत पर इस तालिका पर विचार करें, एक अनुक्रम है:

src=# CREATE TABLE s (a serial PRIMARY KEY, b text);
CREATE TABLE
src=# INSERT INTO s (b) VALUES ('foo'), ('bar'), ('baz');
INSERT 0 3
src=# SELECT * FROM s;
 a |  b
---+-----
 1 | foo
 2 | bar
 3 | baz
(3 rows)

src=# SELECT currval('s_a_seq'), nextval('s_a_seq');
 currval | nextval
---------+---------
       3 |       4
(1 row)

अनुक्रम s_a_seq a . का समर्थन करने के लिए बनाया गया था कॉलम, serial . का type.यह s.a . के लिए स्वत:वृद्धिशील मान उत्पन्न करता है . अब इसे dst में दोहराते हैं , और दूसरी पंक्ति डालें:

dst=# SELECT * FROM s;
 a |  b
---+-----
 1 | foo
 2 | bar
 3 | baz
(3 rows)

dst=# INSERT INTO s (b) VALUES ('foobaz');
ERROR:  duplicate key value violates unique constraint "s_pkey"
DETAIL:  Key (a)=(1) already exists.
dst=#  SELECT currval('s_a_seq'), nextval('s_a_seq');
 currval | nextval
---------+---------
       1 |       2
(1 row)

उफ़, अभी क्या हुआ? गंतव्य ने स्क्रैच से अनुक्रम प्रारंभ करने का प्रयास किया और a . के लिए 1 का मान जेनरेट किया . ऐसा इसलिए है क्योंकि तार्किक प्रतिकृति अनुक्रमों के मानों को दोहराती नहीं है क्योंकि इन अनुक्रमों का अगला मान तालिका में ही संग्रहीत नहीं होता है।

समाधान

यदि आप इसके बारे में तार्किक रूप से सोचते हैं, तो आप द्विदिश सिंक्रनाइज़ेशन के बिना दो स्थानों से समान "ऑटोइनक्रिकमेंट" मान को संशोधित नहीं कर सकते। यदि आपको वास्तव में किसी तालिका की प्रत्येक पंक्ति में एक वृद्धिशील संख्या की आवश्यकता है, और कई सर्वरों से उस तालिका में सम्मिलित करने की आवश्यकता है, तो आप यह कर सकते हैं:

  • संख्या के लिए किसी बाहरी स्रोत का उपयोग करें, जैसे ZooKeeper या etcd,
  • गैर-अतिव्यापी श्रेणियों का उपयोग करें - उदाहरण के लिए, पहला सर्वर 1 से 1 मिलियन की सीमा में संख्याएँ उत्पन्न और सम्मिलित करता है, दूसरा 1 मिलियन से 2 मिलियन की सीमा में, और इसी तरह।

टेबल्स विदाउट यूनिक रोज़

आइए बिना प्राथमिक कुंजी के एक तालिका बनाने और उसकी नकल करने का प्रयास करें:

src=# CREATE TABLE nopk (foo text);
CREATE TABLE
src=# INSERT INTO nopk VALUES ('new york');
INSERT 0 1
src=# INSERT INTO nopk VALUES ('boston');
INSERT 0 1

और पंक्तियाँ अब गंतव्य पर भी हैं:

dst=# SELECT * FROM nopk;
   foo
----------
 new york
 boston
(2 rows)

आइए अब स्रोत पर दूसरी पंक्ति को हटाने का प्रयास करें:

src=# DELETE FROM nopk WHERE foo='boston';
ERROR:  cannot delete from table "nopk" because it does not have a replica identity and publishes deletes
HINT:  To enable deleting from the table, set REPLICA IDENTITY using ALTER TABLE.

ऐसा इसलिए होता है क्योंकि गंतव्य उस पंक्ति की विशिष्ट रूप से पहचान नहीं कर पाएगा जिसे प्राथमिक कुंजी के बिना हटाने (या अपडेट) करने की आवश्यकता है।

समाधान

आप निश्चित रूप से, प्राथमिक कुंजी को शामिल करने के लिए स्कीमा को बदल सकते हैं। यदि आप ऐसा नहीं करना चाहते हैं, तो आप ALTER TABLE और "प्रतिकृति पहचान" को पूर्ण पंक्ति या एक अद्वितीय अनुक्रमणिका पर सेट करें। उदाहरण के लिए:

src=# ALTER TABLE nopk REPLICA IDENTITY FULL;
ALTER TABLE
src=# DELETE FROM nopk WHERE foo='boston';
DELETE 1

हटाना अब सफल है, और प्रतिकृति भी:

dst=# SELECT * FROM nopk;
   foo
----------
 new york
(1 row)

यदि आपकी तालिका में पंक्तियों को विशिष्ट रूप से पहचानने का कोई तरीका नहीं है, तो आप एक बिटस्टक हैं। अधिक जानकारी के लिए ALTERTABLE की प्रतिकृति पहचान अनुभाग देखें।

विभिन्न रूप से विभाजित गंतव्य

क्या यह अच्छा नहीं होगा कि एक ऐसा स्रोत हो जो एक तरह से विभाजित हो और एक अलग तरीके से गंतव्य हो? उदाहरण के लिए, स्रोत पर हम प्रत्येक महीने के लिए और प्रत्येक वर्ष के लिए गंतव्य पर समानताएं रख सकते हैं। संभवतः गंतव्य एक बड़ी मशीन है, और हमें ऐतिहासिक डेटा रखने की आवश्यकता है, लेकिन उस डेटा की शायद ही कभी आवश्यकता होती है।

आइए स्रोत पर एक मासिक-विभाजित तालिका बनाएं:

src=# CREATE TABLE measurement (
src(#     logdate         date not null,
src(#     peaktemp        int
src(# ) PARTITION BY RANGE (logdate);
CREATE TABLE
src=#
src=# CREATE TABLE measurement_y2019m01 PARTITION OF measurement
src-# FOR VALUES FROM ('2019-01-01') TO ('2019-02-01');
CREATE TABLE
src=#
src=# CREATE TABLE measurement_y2019m02 PARTITION OF measurement
src-# FOR VALUES FROM ('2019-02-01') TO ('2019-03-01');
CREATE TABLE
src=#
src=# GRANT SELECT ON measurement, measurement_y2019m01, measurement_y2019m02 TO repuser;
GRANT

और गंतव्य पर वार्षिक-विभाजित तालिका बनाने का प्रयास करें:

dst=# CREATE TABLE measurement (
dst(#     logdate         date not null,
dst(#     peaktemp        int
dst(# ) PARTITION BY RANGE (logdate);
CREATE TABLE
dst=#
dst=# CREATE TABLE measurement_y2018 PARTITION OF measurement
dst-# FOR VALUES FROM ('2018-01-01') TO ('2019-01-01');
CREATE TABLE
dst=#
dst=# CREATE TABLE measurement_y2019 PARTITION OF measurement
dst-# FOR VALUES FROM ('2019-01-01') TO ('2020-01-01');
CREATE TABLE
dst=#
dst=# ALTER SUBSCRIPTION mysub REFRESH PUBLICATION;
ERROR:  relation "public.measurement_y2019m01" does not exist
dst=#

पोस्टग्रेज की शिकायत है कि उसे जनवरी 2019 के लिए विभाजन तालिका की आवश्यकता है, जिसे गंतव्य पर बनाने का हमारा कोई इरादा नहीं है।

ऐसा इसलिए होता है क्योंकि तार्किक प्रतिकृति बेस टेबल स्तर पर नहीं, बल्कि चाइल्ड टेबल स्तर पर काम करती है। इसके लिए कोई वास्तविक समाधान नहीं है - यदि आप विभाजन का पुन:उपयोग कर रहे हैं, तो विभाजन पदानुक्रम समान प्रतिकृति सेटअप के दोनों ओर समान होना चाहिए।

बड़ी वस्तुएं

तार्किक प्रतिकृति का उपयोग करके बड़ी वस्तुओं को दोहराया नहीं जा सकता है। यह शायद आजकल कोई बड़ी बात नहीं है, क्योंकि बड़ी वस्तुओं का भंडारण करना आधुनिक समय की सामान्य प्रथा नहीं है। किसी बाहरी, अनावश्यक भंडारण (जैसे NFS, S3 आदि) पर एक बड़ी वस्तु के संदर्भ को संग्रहीत करना और वस्तु को स्वयं संग्रहीत और दोहराने के बजाय उस संदर्भ को दोहराना भी आसान है।


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. PostgreSQL ट्यूनिंग:प्रदर्शन बढ़ाने के लिए मुख्य बातें

  2. क्लाउड विक्रेता डीप-डाइव:Google क्लाउड प्लेटफ़ॉर्म (GCP) पर PostgreSQL

  3. एन्कोडिंग UTF8 लोकेल en_US से मेल नहीं खाता; चुनी गई LC_CTYPE सेटिंग के लिए LATIN1 एन्कोडिंग की आवश्यकता होती है

  4. पोस्टग्रेस्क्ल में काम नहीं कर रहा है

  5. PostgreSQL के लिए अपग्रेड प्रक्रिया का स्वचालित परीक्षण