పోస్ట్‌గ్రెస్: ఉబ్బు, pg_repack మరియు వాయిదా వేసిన పరిమితులు

పోస్ట్‌గ్రెస్: ఉబ్బు, pg_repack మరియు వాయిదా వేసిన పరిమితులు

పట్టికలు మరియు సూచికలపై ఉబ్బరం యొక్క ప్రభావం విస్తృతంగా తెలుసు మరియు పోస్ట్‌గ్రెస్‌లో మాత్రమే కాదు. VACUUM FULL లేదా CLUSTER వంటి బాక్స్ వెలుపల దీన్ని ఎదుర్కోవడానికి మార్గాలు ఉన్నాయి, కానీ అవి ఆపరేషన్ సమయంలో పట్టికలను లాక్ చేస్తాయి మరియు అందువల్ల ఎల్లప్పుడూ ఉపయోగించబడవు.

ఉబ్బరం ఎలా ఏర్పడుతుంది, మీరు దానిని ఎలా పోరాడవచ్చు, వాయిదా వేసిన అడ్డంకులు మరియు pg_repack పొడిగింపును ఉపయోగించడంలో వారు తీసుకువచ్చే సమస్యల గురించి వ్యాసంలో ఒక చిన్న సిద్ధాంతం ఉంటుంది.

ఈ వ్యాసం ఆధారంగా వ్రాయబడింది నా ప్రసంగం PgConf.Russia 2020లో.

ఉబ్బరం ఎందుకు వస్తుంది?

పోస్ట్‌గ్రెస్ బహుళ-వెర్షన్ మోడల్‌పై ఆధారపడి ఉంటుంది (MVCC) దీని సారాంశం ఏమిటంటే, పట్టికలోని ప్రతి అడ్డు వరుస అనేక సంస్కరణలను కలిగి ఉంటుంది, అయితే లావాదేవీలు ఈ సంస్కరణల్లో ఒకటి కంటే ఎక్కువ చూడవు, కానీ తప్పనిసరిగా ఒకే ఒకటి కాదు. ఇది అనేక లావాదేవీలు ఏకకాలంలో పని చేయడానికి అనుమతిస్తుంది మరియు వాస్తవంగా ఒకదానిపై ఒకటి ప్రభావం చూపదు.

సహజంగానే, ఈ సంస్కరణలన్నీ నిల్వ చేయబడాలి. పోస్ట్‌గ్రెస్ పేజీల వారీగా మెమరీతో పనిచేస్తుంది మరియు పేజీ అనేది డిస్క్ నుండి చదవగలిగే లేదా వ్రాయగలిగే కనీస డేటా. ఇది ఎలా జరుగుతుందో అర్థం చేసుకోవడానికి ఒక చిన్న ఉదాహరణ చూద్దాం.

మేము అనేక రికార్డులను జోడించిన పట్టికను కలిగి ఉన్నామని చెప్పండి. పట్టిక నిల్వ చేయబడిన ఫైల్ యొక్క మొదటి పేజీలో కొత్త డేటా కనిపించింది. ఇవి కమిట్ అయిన తర్వాత ఇతర లావాదేవీలకు అందుబాటులో ఉండే వరుసల లైవ్ వెర్షన్‌లు (సరళత కోసం, ఐసోలేషన్ స్థాయి రీడ్ కమిటెడ్ అని మేము ఊహిస్తాము).

పోస్ట్‌గ్రెస్: ఉబ్బు, pg_repack మరియు వాయిదా వేసిన పరిమితులు

మేము ఎంట్రీలలో ఒకదాన్ని అప్‌డేట్ చేసాము, తద్వారా పాత వెర్షన్ ఇకపై సంబంధితంగా లేదని గుర్తు పెట్టాము.

పోస్ట్‌గ్రెస్: ఉబ్బు, pg_repack మరియు వాయిదా వేసిన పరిమితులు

దశల వారీగా, వరుస సంస్కరణలను నవీకరించడం మరియు తొలగించడం, మేము దాదాపు సగం డేటా "చెత్త" ఉన్న పేజీతో ముగించాము. ఈ డేటా ఏ లావాదేవీకి కనిపించదు.

పోస్ట్‌గ్రెస్: ఉబ్బు, pg_repack మరియు వాయిదా వేసిన పరిమితులు

Postgres ఒక యంత్రాంగాన్ని కలిగి ఉంది వాక్యూమ్, ఇది వాడుకలో లేని సంస్కరణలను శుభ్రపరుస్తుంది మరియు కొత్త డేటా కోసం స్థలాన్ని చేస్తుంది. కానీ ఇది తగినంత దూకుడుగా కాన్ఫిగర్ చేయబడకపోతే లేదా ఇతర పట్టికలలో పని చేయడంలో బిజీగా ఉంటే, అప్పుడు “చెత్త డేటా” మిగిలి ఉంటుంది మరియు మేము కొత్త డేటా కోసం అదనపు పేజీలను ఉపయోగించాలి.

కాబట్టి మా ఉదాహరణలో, ఏదో ఒక సమయంలో పట్టిక నాలుగు పేజీలను కలిగి ఉంటుంది, కానీ అందులో సగం మాత్రమే ప్రత్యక్ష డేటాను కలిగి ఉంటుంది. ఫలితంగా, పట్టికను యాక్సెస్ చేస్తున్నప్పుడు, మేము అవసరమైన దానికంటే ఎక్కువ డేటాను చదువుతాము.

పోస్ట్‌గ్రెస్: ఉబ్బు, pg_repack మరియు వాయిదా వేసిన పరిమితులు

VACUUM ఇప్పుడు అన్ని అసంబద్ధమైన వరుస సంస్కరణలను తొలగించినప్పటికీ, పరిస్థితి నాటకీయంగా మెరుగుపడదు. మేము కొత్త అడ్డు వరుసల కోసం పేజీలలో లేదా మొత్తం పేజీలలో ఖాళీ స్థలాన్ని కలిగి ఉంటాము, కానీ మేము ఇంకా అవసరమైన దానికంటే ఎక్కువ డేటాను చదువుతూ ఉంటాము.
మార్గం ద్వారా, ఫైల్ చివరిలో పూర్తిగా ఖాళీ పేజీ (మా ఉదాహరణలో రెండవది) ఉంటే, అప్పుడు VACUUM దానిని ట్రిమ్ చేయగలదు. కానీ ఇప్పుడు ఆమె మధ్యలో ఉంది కాబట్టి ఆమెను ఏమీ చేయలేము.

పోస్ట్‌గ్రెస్: ఉబ్బు, pg_repack మరియు వాయిదా వేసిన పరిమితులు

అటువంటి ఖాళీ లేదా చాలా తక్కువ పేజీల సంఖ్య పెద్దగా మారినప్పుడు, దీనిని బ్లోట్ అని పిలుస్తారు, ఇది పనితీరును ప్రభావితం చేయడం ప్రారంభిస్తుంది.

పైన వివరించిన ప్రతిదీ పట్టికలలో ఉబ్బు సంభవించే మెకానిక్స్. సూచికలలో ఇది దాదాపు అదే విధంగా జరుగుతుంది.

నాకు ఉబ్బరం ఉందా?

మీకు ఉబ్బరం ఉందో లేదో తెలుసుకోవడానికి అనేక మార్గాలు ఉన్నాయి. మొదటి ఆలోచన అంతర్గత పోస్ట్‌గ్రెస్ గణాంకాలను ఉపయోగించడం, ఇందులో పట్టికలలోని వరుసల సంఖ్య, “లైవ్” వరుసల సంఖ్య మొదలైన వాటి గురించి సుమారు సమాచారం ఉంటుంది. మీరు ఇంటర్నెట్‌లో రెడీమేడ్ స్క్రిప్ట్‌ల యొక్క అనేక వైవిధ్యాలను కనుగొనవచ్చు. మేము ప్రాతిపదికగా తీసుకున్నాము స్క్రిప్ట్ PostgreSQL నిపుణుల నుండి, ఇది టోస్ట్ మరియు బ్లోట్ btree సూచికలతో పాటు బ్లోట్ టేబుల్‌లను అంచనా వేయగలదు. మా అనుభవంలో, దాని లోపం 10-20%.

పొడిగింపును ఉపయోగించడం మరొక మార్గం pgstattuple, ఇది మీరు పేజీల లోపల చూడడానికి మరియు అంచనా మరియు ఖచ్చితమైన బ్లోట్ విలువ రెండింటినీ పొందడానికి అనుమతిస్తుంది. కానీ రెండవ సందర్భంలో, మీరు మొత్తం పట్టికను స్కాన్ చేయాలి.

మేము ఒక చిన్న బ్లోట్ విలువను, 20% వరకు, ఆమోదయోగ్యమైనదిగా పరిగణిస్తాము. ఇది పూరక కారకాల యొక్క అనలాగ్‌గా పరిగణించబడుతుంది పట్టికలు и సూచీలు. 50% మరియు అంతకంటే ఎక్కువ వద్ద, పనితీరు సమస్యలు ప్రారంభమవుతాయి.

ఉబ్బరంతో పోరాడటానికి మార్గాలు

పోస్ట్‌గ్రెస్‌లో ఉబ్బరంతో వ్యవహరించడానికి అనేక మార్గాలు ఉన్నాయి, కానీ అవి ఎల్లప్పుడూ అందరికీ అనుకూలంగా ఉండవు.

AUTOVACUUMని కాన్ఫిగర్ చేయండి, తద్వారా ఉబ్బరం జరగదు. లేదా మరింత ఖచ్చితంగా, మీకు ఆమోదయోగ్యమైన స్థాయిలో ఉంచడానికి. ఇది "కెప్టెన్" సలహా లాగా ఉంది, కానీ వాస్తవానికి ఇది సాధించడం ఎల్లప్పుడూ సులభం కాదు. ఉదాహరణకు, మీరు డేటా స్కీమాకు సాధారణ మార్పులతో సక్రియ అభివృద్ధిని కలిగి ఉన్నారు లేదా కొన్ని రకాల డేటా మైగ్రేషన్ జరుగుతోంది. ఫలితంగా, మీ లోడ్ ప్రొఫైల్ తరచుగా మారవచ్చు మరియు సాధారణంగా పట్టిక నుండి పట్టికకు మారుతూ ఉంటుంది. దీనర్థం మీరు నిరంతరం కొంచెం ముందుకు పని చేయాలి మరియు ప్రతి టేబుల్ యొక్క మారుతున్న ప్రొఫైల్‌కు AUTOVACUUMని సర్దుబాటు చేయాలి. కానీ స్పష్టంగా దీన్ని చేయడం సులభం కాదు.

AUTOVACUUM పట్టికలను కొనసాగించలేకపోవడానికి మరొక సాధారణ కారణం ఏమిటంటే, ఆ లావాదేవీలకు అందుబాటులో ఉన్న డేటాను క్లీన్ చేయకుండా నిరోధించే దీర్ఘకాల లావాదేవీలు ఉన్నాయి. ఇక్కడ సిఫార్సు కూడా స్పష్టంగా ఉంది - "డాంగ్లింగ్" లావాదేవీలను వదిలించుకోండి మరియు క్రియాశీల లావాదేవీల సమయాన్ని తగ్గించండి. మీ అప్లికేషన్‌పై లోడ్ OLAP మరియు OLTP యొక్క హైబ్రిడ్ అయితే, మీరు ఏకకాలంలో అనేక తరచుగా నవీకరణలు మరియు చిన్న ప్రశ్నలతో పాటు దీర్ఘకాలిక కార్యకలాపాలను కలిగి ఉండవచ్చు - ఉదాహరణకు, నివేదికను రూపొందించడం. అటువంటి పరిస్థితిలో, వివిధ స్థావరాలలో లోడ్ను వ్యాప్తి చేయడం గురించి ఆలోచించడం విలువైనది, ఇది వాటిలో ప్రతి ఒక్కటి మరింత చక్కగా ట్యూనింగ్ చేయడానికి అనుమతిస్తుంది.

మరొక ఉదాహరణ - ప్రొఫైల్ సజాతీయంగా ఉన్నప్పటికీ, డేటాబేస్ చాలా ఎక్కువ లోడ్‌లో ఉన్నప్పటికీ, చాలా దూకుడుగా ఉండే AUTOVACUUM కూడా భరించకపోవచ్చు మరియు ఉబ్బరం ఏర్పడుతుంది. స్కేలింగ్ (నిలువు లేదా క్షితిజ సమాంతర) మాత్రమే పరిష్కారం.

మీరు AUTOVACUUMని సెటప్ చేసిన పరిస్థితిలో ఏమి చేయాలి, కానీ ఉబ్బరం పెరుగుతూనే ఉంది.

జట్టు వాక్యూమ్ ఫుల్ పట్టికలు మరియు సూచికల కంటెంట్‌లను పునర్నిర్మిస్తుంది మరియు వాటిలో సంబంధిత డేటాను మాత్రమే వదిలివేస్తుంది. ఉబ్బును తొలగించడానికి, ఇది ఖచ్చితంగా పని చేస్తుంది, కానీ దాని అమలు సమయంలో టేబుల్‌పై ప్రత్యేకమైన లాక్ సంగ్రహించబడుతుంది (యాక్సెస్‌ఎక్స్‌క్లూజివ్‌లాక్), ఇది ఈ టేబుల్‌పై ప్రశ్నలను అమలు చేయడానికి అనుమతించదు, ఎంచుకుంటుంది. మీరు కొంత సమయం పాటు మీ సేవను లేదా దానిలో కొంత భాగాన్ని నిలిపివేయగలిగితే (డేటాబేస్ మరియు మీ హార్డ్‌వేర్ పరిమాణంపై ఆధారపడి పదుల నిమిషాల నుండి చాలా గంటల వరకు), అప్పుడు ఈ ఎంపిక ఉత్తమమైనది. దురదృష్టవశాత్తూ, షెడ్యూల్ చేయబడిన నిర్వహణ సమయంలో VACUUM FULLని అమలు చేయడానికి మాకు సమయం లేదు, కాబట్టి ఈ పద్ధతి మాకు తగినది కాదు.

జట్టు క్లస్టర్ VACUUM FULL వలె పట్టికల కంటెంట్‌లను పునర్నిర్మిస్తుంది, అయితే డిస్క్‌లో డేటా భౌతికంగా ఆర్డర్ చేయబడే సూచికను పేర్కొనడానికి మిమ్మల్ని అనుమతిస్తుంది (కానీ భవిష్యత్తులో ఆర్డర్ కొత్త అడ్డు వరుసలకు హామీ ఇవ్వబడదు). నిర్దిష్ట పరిస్థితుల్లో, ఇది అనేక ప్రశ్నలకు మంచి ఆప్టిమైజేషన్ - ఇండెక్స్ ద్వారా బహుళ రికార్డులను చదవడం. కమాండ్ యొక్క ప్రతికూలత VACUUM FULL వలె ఉంటుంది - ఇది ఆపరేషన్ సమయంలో పట్టికను లాక్ చేస్తుంది.

జట్టు REINDEX మునుపటి రెండు మాదిరిగానే, కానీ నిర్దిష్ట సూచిక లేదా పట్టికలోని అన్ని సూచికలను పునర్నిర్మిస్తుంది. తాళాలు కొద్దిగా బలహీనంగా ఉన్నాయి: టేబుల్‌పై షేర్‌లాక్ (మార్పులను నిరోధిస్తుంది, కానీ ఎంపికను అనుమతిస్తుంది) మరియు పునర్నిర్మించబడుతున్న ఇండెక్స్‌లో AccessExclusiveLock (ఈ సూచికను ఉపయోగించి ప్రశ్నలను బ్లాక్ చేస్తుంది). అయితే, పోస్ట్‌గ్రెస్ యొక్క 12వ సంస్కరణలో ఒక పరామితి కనిపించింది ఏకకాలంలో, ఇది ఏకకాలిక జోడింపు, సవరణ లేదా రికార్డుల తొలగింపును నిరోధించకుండా ఇండెక్స్‌ను పునర్నిర్మించడానికి మిమ్మల్ని అనుమతిస్తుంది.

Postgres యొక్క మునుపటి సంస్కరణల్లో, మీరు REINDEX ఏకకాలంలో ఉపయోగించిన ఫలితాన్ని సాధించవచ్చు ఇండెక్స్‌ను ఏకకాలంలో సృష్టించండి. ఇది కఠినమైన లాకింగ్ లేకుండా ఇండెక్స్‌ను సృష్టించడానికి మిమ్మల్ని అనుమతిస్తుంది (ShareUpdateExclusiveLock, ఇది సమాంతర ప్రశ్నలకు అంతరాయం కలిగించదు), ఆపై పాత సూచికను కొత్త దానితో భర్తీ చేయండి మరియు పాత సూచికను తొలగించండి. ఇది మీ అప్లికేషన్‌తో జోక్యం చేసుకోకుండా ఇండెక్స్ ఉబ్బును తొలగించడానికి మిమ్మల్ని అనుమతిస్తుంది. ఇండెక్స్‌లను పునర్నిర్మించేటప్పుడు డిస్క్ సబ్‌సిస్టమ్‌పై అదనపు లోడ్ ఉంటుందని పరిగణనలోకి తీసుకోవడం చాలా ముఖ్యం.

అందువల్ల, ఇండెక్స్‌ల కోసం "ఫ్లైలో" ఉబ్బును తొలగించడానికి మార్గాలు ఉంటే, అప్పుడు పట్టికలకు ఏదీ లేదు. ఇక్కడే వివిధ బాహ్య పొడిగింపులు అమలులోకి వస్తాయి: pg_repack (గతంలో pg_reorg), pgcompact, pgcompactable మరియు ఇతరులు. ఈ ఆర్టికల్‌లో, నేను వాటిని సరిపోల్చను మరియు pg_repack గురించి మాత్రమే మాట్లాడతాను, కొన్ని మార్పుల తర్వాత, మనల్ని మనం ఉపయోగిస్తాము.

pg_repack ఎలా పని చేస్తుంది

పోస్ట్‌గ్రెస్: ఉబ్బు, pg_repack మరియు వాయిదా వేసిన పరిమితులు
మేము పూర్తిగా సాధారణ పట్టికను కలిగి ఉన్నామని చెప్పండి - సూచికలు, పరిమితులు మరియు దురదృష్టవశాత్తు, ఉబ్బుతో. pg_repack యొక్క మొదటి దశ అది నడుస్తున్నప్పుడు అన్ని మార్పుల గురించి డేటాను నిల్వ చేయడానికి లాగ్ పట్టికను సృష్టించడం. ప్రతి ఇన్సర్ట్, అప్‌డేట్ మరియు డిలీట్ కోసం ట్రిగ్గర్ ఈ మార్పులను పునరావృతం చేస్తుంది. అప్పుడు ఒక పట్టిక సృష్టించబడుతుంది, నిర్మాణంలో అసలైనదిగా ఉంటుంది, కానీ సూచికలు మరియు పరిమితులు లేకుండా, డేటాను చొప్పించే ప్రక్రియను మందగించకూడదు.

తరువాత, pg_repack పాత పట్టిక నుండి డేటాను కొత్త పట్టికకు బదిలీ చేస్తుంది, అన్ని అసంబద్ధమైన అడ్డు వరుసలను స్వయంచాలకంగా ఫిల్టర్ చేస్తుంది, ఆపై కొత్త పట్టిక కోసం సూచికలను సృష్టిస్తుంది. ఈ అన్ని కార్యకలాపాల అమలు సమయంలో, లాగ్ పట్టికలో మార్పులు పేరుకుపోతాయి.

తదుపరి దశ మార్పులను కొత్త పట్టికకు బదిలీ చేయడం. మైగ్రేషన్ అనేక పునరావృతాల ద్వారా నిర్వహించబడుతుంది మరియు లాగ్ టేబుల్‌లో 20 కంటే తక్కువ ఎంట్రీలు మిగిలి ఉన్నప్పుడు, pg_repack బలమైన లాక్‌ని పొందుతుంది, తాజా డేటాను మైగ్రేట్ చేస్తుంది మరియు పోస్ట్‌గ్రెస్ సిస్టమ్ పట్టికలలో పాత పట్టికను కొత్తదానితో భర్తీ చేస్తుంది. మీరు టేబుల్‌తో పని చేయలేని ఏకైక మరియు చాలా తక్కువ సమయం ఇది. దీని తరువాత, పాత పట్టిక మరియు లాగ్‌లతో ఉన్న పట్టిక తొలగించబడతాయి మరియు ఫైల్ సిస్టమ్‌లో స్థలం ఖాళీ చేయబడుతుంది. ప్రక్రియ పూర్తయింది.

ప్రతిదీ సిద్ధాంతపరంగా చాలా బాగుంది, కానీ ఆచరణలో ఏమి జరుగుతుంది? మేము pg_repackను లోడ్ లేకుండా మరియు లోడ్‌లో పరీక్షించాము మరియు అకాల స్టాప్ విషయంలో దాని ఆపరేషన్‌ను తనిఖీ చేసాము (మరో మాటలో చెప్పాలంటే, Ctrl+C ఉపయోగించి). పరీక్షలన్నీ పాజిటివ్‌గా వచ్చాయి.

మేము ఆహార దుకాణానికి వెళ్లాము - ఆపై మేము ఊహించిన విధంగా ప్రతిదీ జరగలేదు.

మొదటి పాన్కేక్ అమ్మకానికి ఉంది

మొదటి క్లస్టర్‌లో మేము ప్రత్యేకమైన పరిమితిని ఉల్లంఘించడం గురించి లోపాన్ని అందుకున్నాము:

$ ./pg_repack -t tablename -o id
INFO: repacking table "tablename"
ERROR: query failed: 
    ERROR: duplicate key value violates unique constraint "index_16508"
DETAIL:  Key (id, index)=(100500, 42) already exists.

ఈ పరిమితి స్వయంచాలకంగా రూపొందించబడిన పేరు index_16508ని కలిగి ఉంది - ఇది pg_repack ద్వారా సృష్టించబడింది. దాని కూర్పులో చేర్చబడిన లక్షణాల ఆధారంగా, దానికి అనుగుణంగా ఉండే "మా" పరిమితిని మేము నిర్ణయించాము. సమస్య ఇది ​​పూర్తిగా సాధారణ పరిమితి కాదు, వాయిదా వేయబడినది (వాయిదా వేసిన పరిమితి), అనగా. దాని ధృవీకరణ sql కమాండ్ కంటే తరువాత నిర్వహించబడుతుంది, ఇది ఊహించని పరిణామాలకు దారి తీస్తుంది.

వాయిదా వేసిన పరిమితులు: అవి ఎందుకు అవసరం మరియు అవి ఎలా పని చేస్తాయి

వాయిదా వేసిన పరిమితుల గురించి ఒక చిన్న సిద్ధాంతం.
ఒక సాధారణ ఉదాహరణను పరిశీలిద్దాం: మనకు రెండు లక్షణాలతో కూడిన కార్ల టేబుల్-రిఫరెన్స్ బుక్ ఉంది - డైరెక్టరీలో కారు పేరు మరియు క్రమం.
పోస్ట్‌గ్రెస్: ఉబ్బు, pg_repack మరియు వాయిదా వేసిన పరిమితులు

create table cars
(
  name text constraint pk_cars primary key,
  ord integer not null constraint uk_cars unique
);



మేము మొదటి మరియు రెండవ కార్లను మార్చుకోవాల్సిన అవసరం ఉందని చెప్పండి. మొదటి విలువను రెండవదానికి మరియు రెండవది మొదటిదానికి అప్‌డేట్ చేయడం సరళమైన పరిష్కారం:

begin;
  update cars set ord = 2 where name = 'audi';
  update cars set ord = 1 where name = 'bmw';
commit;

కానీ మేము ఈ కోడ్‌ని అమలు చేసినప్పుడు, మేము పరిమితి ఉల్లంఘనను ఆశిస్తున్నాము ఎందుకంటే పట్టికలోని విలువల క్రమం ప్రత్యేకంగా ఉంటుంది:

[23305] ERROR: duplicate key value violates unique constraint “uk_cars”
Detail: Key (ord)=(2) already exists.

నేను దీన్ని భిన్నంగా ఎలా చేయగలను? ఎంపిక ఒకటి: పట్టికలో ఉనికిలో లేదని హామీ ఇవ్వబడిన ఆర్డర్‌కు అదనపు విలువ భర్తీని జోడించండి, ఉదాహరణకు “-1”. ప్రోగ్రామింగ్‌లో, దీనిని "రెండు వేరియబుల్స్ విలువలను మూడవ వంతు ద్వారా మార్పిడి చేయడం" అంటారు. ఈ పద్ధతి యొక్క ఏకైక లోపం అదనపు నవీకరణ.

ఎంపిక రెండు: పూర్ణాంకాలకు బదులుగా ఆర్డర్ విలువ కోసం ఫ్లోటింగ్ పాయింట్ డేటా రకాన్ని ఉపయోగించడానికి టేబుల్‌ని రీడిజైన్ చేయండి. అప్పుడు, 1 నుండి విలువను నవీకరిస్తున్నప్పుడు, ఉదాహరణకు, 2.5కి, మొదటి ఎంట్రీ స్వయంచాలకంగా రెండవ మరియు మూడవ వాటి మధ్య "నిలబడి ఉంటుంది". ఈ పరిష్కారం పనిచేస్తుంది, కానీ రెండు పరిమితులు ఉన్నాయి. ముందుగా, ఇంటర్‌ఫేస్‌లో ఎక్కడా విలువ ఉపయోగించబడితే అది మీ కోసం పని చేయదు. రెండవది, డేటా రకం యొక్క ఖచ్చితత్వాన్ని బట్టి, మీరు అన్ని రికార్డుల విలువలను తిరిగి లెక్కించే ముందు పరిమిత సంఖ్యలో సాధ్యమయ్యే ఇన్సర్ట్‌లను కలిగి ఉంటారు.

ఎంపిక మూడు: నిర్బంధాన్ని వాయిదా వేయండి, తద్వారా ఇది కట్టుబడి ఉన్న సమయంలో మాత్రమే తనిఖీ చేయబడుతుంది:

create table cars
(
  name text constraint pk_cars primary key,
  ord integer not null constraint uk_cars unique deferrable initially deferred
);

మా ప్రారంభ అభ్యర్థన యొక్క తర్కం కట్టుబడి సమయంలో అన్ని విలువలు ప్రత్యేకంగా ఉన్నాయని నిర్ధారిస్తుంది కాబట్టి, అది విజయవంతమవుతుంది.

పైన చర్చించిన ఉదాహరణ, వాస్తవానికి, చాలా సింథటిక్, కానీ ఇది ఆలోచనను వెల్లడిస్తుంది. మా అప్లికేషన్‌లో, బోర్డ్‌లోని షేర్డ్ విడ్జెట్ ఆబ్జెక్ట్‌లతో వినియోగదారులు ఏకకాలంలో పని చేసినప్పుడు వైరుధ్యాలను పరిష్కరించడానికి బాధ్యత వహించే లాజిక్‌ను అమలు చేయడానికి మేము వాయిదా వేసిన పరిమితులను ఉపయోగిస్తాము. అటువంటి పరిమితులను ఉపయోగించడం వలన అప్లికేషన్ కోడ్‌ను కొంచెం సరళంగా మార్చవచ్చు.

సాధారణంగా, నిర్బంధ రకాన్ని బట్టి, పోస్ట్‌గ్రెస్ వాటిని తనిఖీ చేయడానికి మూడు స్థాయిల గ్రాన్యులారిటీని కలిగి ఉంటుంది: వరుస, లావాదేవీ మరియు వ్యక్తీకరణ స్థాయిలు.
పోస్ట్‌గ్రెస్: ఉబ్బు, pg_repack మరియు వాయిదా వేసిన పరిమితులు
మూలం: బెగ్రిఫ్స్

చెక్ మరియు NULL ఎల్లప్పుడూ వరుస స్థాయిలో తనిఖీ చేయబడతాయి; ఇతర పరిమితుల కోసం, టేబుల్ నుండి చూడగలిగే విధంగా, విభిన్న ఎంపికలు ఉన్నాయి. మీరు మరింత చదవగలరు ఇక్కడ.

క్లుప్తంగా క్లుప్తంగా చెప్పాలంటే, అనేక సందర్భాల్లో వాయిదా వేసిన పరిమితులు మరింత చదవగలిగే కోడ్ మరియు తక్కువ ఆదేశాలను అందిస్తాయి. అయితే, డీబగ్గింగ్ ప్రక్రియను క్లిష్టతరం చేయడం ద్వారా మీరు దీని కోసం చెల్లించాలి, ఎందుకంటే లోపం సంభవించిన క్షణం మరియు దాని గురించి మీరు కనుగొన్న క్షణం సమయానికి వేరు చేయబడుతుంది. మరొక సంభావ్య సమస్య ఏమిటంటే, అభ్యర్థన వాయిదా వేయబడిన పరిమితిని కలిగి ఉన్నట్లయితే, షెడ్యూలర్ ఎల్లప్పుడూ సరైన ప్రణాళికను రూపొందించలేకపోవచ్చు.

pg_repack మెరుగుదల

వాయిదా వేసిన పరిమితులు ఏమిటో మేము కవర్ చేసాము, కానీ అవి మన సమస్యకు ఎలా సంబంధం కలిగి ఉంటాయి? ఇంతకు ముందు మనం అందుకున్న లోపాన్ని గుర్తుచేసుకుందాం:

$ ./pg_repack -t tablename -o id
INFO: repacking table "tablename"
ERROR: query failed: 
    ERROR: duplicate key value violates unique constraint "index_16508"
DETAIL:  Key (id, index)=(100500, 42) already exists.

లాగ్ టేబుల్ నుండి కొత్త టేబుల్‌కి డేటా కాపీ చేయబడినప్పుడు ఇది జరుగుతుంది. ఇది వింతగా కనిపిస్తుంది ఎందుకంటే... లాగ్ టేబుల్‌లోని డేటా సోర్స్ టేబుల్‌లోని డేటాతో పాటు కట్టుబడి ఉంటుంది. అసలు పట్టికలోని పరిమితులను వారు సంతృప్తి పరచినట్లయితే, వారు కొత్తదానిలో అదే పరిమితులను ఎలా ఉల్లంఘిస్తారు?

ఇది ముగిసినట్లుగా, సమస్య యొక్క మూలం pg_repack యొక్క మునుపటి దశలో ఉంది, ఇది సూచికలను మాత్రమే సృష్టిస్తుంది, కానీ పరిమితులను కాదు: పాత పట్టికకు ప్రత్యేకమైన పరిమితి ఉంది మరియు కొత్తది బదులుగా ప్రత్యేక సూచికను సృష్టించింది.

పోస్ట్‌గ్రెస్: ఉబ్బు, pg_repack మరియు వాయిదా వేసిన పరిమితులు

పరిమితి సాధారణమైనది మరియు వాయిదా వేయబడకపోతే, బదులుగా సృష్టించబడిన ఏకైక సూచిక ఈ పరిమితికి సమానం అని ఇక్కడ గమనించడం ముఖ్యం, ఎందుకంటే ప్రత్యేక సూచికను సృష్టించడం ద్వారా పోస్ట్‌గ్రెస్‌లోని ప్రత్యేక పరిమితులు అమలు చేయబడతాయి. కానీ వాయిదా వేసిన పరిమితి విషయంలో, ప్రవర్తన ఒకేలా ఉండదు, ఎందుకంటే ఇండెక్స్ వాయిదా వేయబడదు మరియు sql కమాండ్ అమలు చేయబడిన సమయంలో ఎల్లప్పుడూ తనిఖీ చేయబడుతుంది.

అందువల్ల, సమస్య యొక్క సారాంశం చెక్ యొక్క "ఆలస్యం" లో ఉంటుంది: అసలు పట్టికలో ఇది కమిట్ అయిన సమయంలో మరియు కొత్త పట్టికలో sql కమాండ్ అమలు చేయబడిన సమయంలో జరుగుతుంది. దీనర్థం రెండు సందర్భాల్లోనూ తనిఖీలు ఒకే విధంగా నిర్వహించబడుతున్నాయని మేము నిర్ధారించుకోవాలి: ఎల్లప్పుడూ ఆలస్యం లేదా ఎల్లప్పుడూ వెంటనే.

కాబట్టి మనకు ఏ ఆలోచనలు ఉన్నాయి?

వాయిదా వేసినట్లుగా ఒక సూచికను సృష్టించండి

రెండు తనిఖీలను తక్షణ మోడ్‌లో నిర్వహించడం మొదటి ఆలోచన. ఇది అనేక తప్పుడు సానుకూల పరిమితులను సృష్టించవచ్చు, కానీ వాటిలో కొన్ని ఉంటే, ఇది వినియోగదారుల పనిని ప్రభావితం చేయకూడదు, ఎందుకంటే అలాంటి వైరుధ్యాలు వారికి సాధారణ పరిస్థితి. ఉదాహరణకు, ఇద్దరు వినియోగదారులు ఒకే సమయంలో ఒకే విడ్జెట్‌ను సవరించడం ప్రారంభించినప్పుడు మరియు రెండవ వినియోగదారు యొక్క క్లయింట్‌కు మొదటి వినియోగదారు సవరించడం కోసం విడ్జెట్ ఇప్పటికే బ్లాక్ చేయబడిందని సమాచారాన్ని స్వీకరించడానికి సమయం లేనప్పుడు అవి సంభవిస్తాయి. అటువంటి పరిస్థితిలో, సర్వర్ రెండవ వినియోగదారుని నిరాకరిస్తుంది మరియు దాని క్లయింట్ మార్పులను వెనక్కి తీసుకుంటుంది మరియు విడ్జెట్‌ను బ్లాక్ చేస్తుంది. కొద్దిసేపటి తర్వాత, మొదటి వినియోగదారు సవరణను పూర్తి చేసినప్పుడు, రెండవది విడ్జెట్ ఇకపై బ్లాక్ చేయబడదని మరియు వారి చర్యను పునరావృతం చేయగలదని సమాచారాన్ని అందుకుంటారు.

పోస్ట్‌గ్రెస్: ఉబ్బు, pg_repack మరియు వాయిదా వేసిన పరిమితులు

తనిఖీలు ఎల్లప్పుడూ నాన్-డిఫర్డ్ మోడ్‌లో ఉండేలా చూసుకోవడానికి, మేము అసలు వాయిదా వేసిన పరిమితిని పోలిన కొత్త సూచికను సృష్టించాము:

CREATE UNIQUE INDEX CONCURRENTLY uk_tablename__immediate ON tablename (id, index);
-- run pg_repack
DROP INDEX CONCURRENTLY uk_tablename__immediate;

పరీక్ష వాతావరణంలో, మేము ఊహించిన కొన్ని లోపాలను మాత్రమే అందుకున్నాము. విజయం! మేము ఉత్పత్తిపై మళ్లీ pg_repackని అమలు చేసాము మరియు ఒక గంట పనిలో మొదటి క్లస్టర్‌లో 5 ఎర్రర్‌లను పొందాము. ఇది ఆమోదయోగ్యమైన ఫలితం. అయినప్పటికీ, ఇప్పటికే రెండవ క్లస్టర్‌లో లోపాల సంఖ్య గణనీయంగా పెరిగింది మరియు మేము pg_repackని ఆపవలసి వచ్చింది.

ఎందుకు జరిగింది? ఒకే సమయంలో ఒకే విడ్జెట్‌లతో ఎంత మంది వినియోగదారులు పనిచేస్తున్నారనే దానిపై లోపం సంభవించే సంభావ్యత ఆధారపడి ఉంటుంది. స్పష్టంగా, ఆ సమయంలో ఇతర వాటి కంటే మొదటి క్లస్టర్‌లో నిల్వ చేయబడిన డేటాతో చాలా తక్కువ పోటీ మార్పులు ఉన్నాయి, అనగా. మేము కేవలం "అదృష్టవంతులం".

ఆలోచన ఫలించలేదు. ఆ సమయంలో, మేము రెండు ఇతర పరిష్కారాలను చూశాము: వాయిదా వేసిన పరిమితులను తొలగించడానికి మా అప్లికేషన్ కోడ్‌ని మళ్లీ వ్రాయండి లేదా వాటితో పని చేయడానికి pg_repackను “బోధించండి”. మేము రెండవదాన్ని ఎంచుకున్నాము.

కొత్త పట్టికలోని సూచికలను అసలు పట్టిక నుండి వాయిదా వేసిన పరిమితులతో భర్తీ చేయండి

పునర్విమర్శ యొక్క ఉద్దేశ్యం స్పష్టంగా ఉంది - అసలు పట్టికలో వాయిదా వేసిన పరిమితి ఉన్నట్లయితే, కొత్తదాని కోసం మీరు అటువంటి పరిమితిని సృష్టించాలి మరియు సూచిక కాదు.

మా మార్పులను పరీక్షించడానికి, మేము ఒక సాధారణ పరీక్షను వ్రాసాము:

  • వాయిదా వేసిన పరిమితి మరియు ఒక రికార్డుతో పట్టిక;
  • ఇప్పటికే ఉన్న రికార్డ్‌తో విభేదించే లూప్‌లో డేటాను చొప్పించండి;
  • నవీకరణ చేయండి - డేటా ఇకపై విభేదించదు;
  • మార్పులకు కట్టుబడి.

create table test_table
(
  id serial,
  val int,
  constraint uk_test_table__val unique (val) deferrable initially deferred 
);

INSERT INTO test_table (val) VALUES (0);
FOR i IN 1..10000 LOOP
  BEGIN
    INSERT INTO test_table VALUES (0) RETURNING id INTO v_id;
    UPDATE test_table set val = i where id = v_id;
    COMMIT;
  END;
END LOOP;

pg_repack యొక్క అసలైన సంస్కరణ ఎల్లప్పుడూ మొదటి ఇన్సర్ట్‌లో క్రాష్ అవుతుంది, సవరించిన సంస్కరణ లోపాలు లేకుండా పని చేస్తుంది. గొప్ప.

మేము ఉత్పత్తికి వెళ్తాము మరియు లాగ్ టేబుల్ నుండి డేటాను కొత్తదానికి కాపీ చేసే దశలో మళ్లీ లోపం ఏర్పడుతుంది:

$ ./pg_repack -t tablename -o id
INFO: repacking table "tablename"
ERROR: query failed: 
    ERROR: duplicate key value violates unique constraint "index_16508"
DETAIL:  Key (id, index)=(100500, 42) already exists.

క్లాసిక్ పరిస్థితి: ప్రతిదీ పరీక్ష వాతావరణంలో పనిచేస్తుంది, కానీ ఉత్పత్తిలో కాదు?!

APPLY_COUNT మరియు రెండు బ్యాచ్‌ల జంక్షన్

మేము కోడ్‌ని అక్షరాలా పంక్తి వారీగా విశ్లేషించడం ప్రారంభించాము మరియు ఒక ముఖ్యమైన అంశాన్ని కనుగొన్నాము: డేటా లాగ్ టేబుల్ నుండి బ్యాచ్‌లలో కొత్తదానికి బదిలీ చేయబడుతుంది, APPLY_COUNT స్థిరాంకం బ్యాచ్ పరిమాణాన్ని సూచిస్తుంది:

for (;;)
{
num = apply_log(connection, table, APPLY_COUNT);

if (num > MIN_TUPLES_BEFORE_SWITCH)
     continue;  /* there might be still some tuples, repeat. */
...
}

సమస్య ఏమిటంటే, అసలు లావాదేవీ నుండి డేటా, అనేక కార్యకలాపాలు పరిమితిని ఉల్లంఘించగలవు, బదిలీ చేయబడినప్పుడు, రెండు బ్యాచ్‌ల జంక్షన్‌లో ముగుస్తుంది - సగం కమాండ్‌లు మొదటి బ్యాచ్‌లో కట్టుబడి ఉంటాయి మరియు మిగిలిన సగం రెండవది. మరియు ఇక్కడ, మీ అదృష్టాన్ని బట్టి: మొదటి బ్యాచ్‌లో జట్లు ఏదైనా ఉల్లంఘించకపోతే, ప్రతిదీ బాగానే ఉంటుంది, కానీ వారు చేస్తే, లోపం సంభవిస్తుంది.

APPLY_COUNT అనేది 1000 రికార్డ్‌లకు సమానం, ఇది మా పరీక్షలు ఎందుకు విజయవంతమయ్యాయో వివరిస్తుంది - అవి “బ్యాచ్ జంక్షన్” కేసును కవర్ చేయలేదు. మేము రెండు ఆదేశాలను ఉపయోగించాము - చొప్పించండి మరియు నవీకరించండి, కాబట్టి ఖచ్చితంగా రెండు ఆదేశాల యొక్క 500 లావాదేవీలు ఎల్లప్పుడూ బ్యాచ్‌లో ఉంచబడతాయి మరియు మేము ఎటువంటి సమస్యలను ఎదుర్కోలేదు. రెండవ నవీకరణను జోడించిన తర్వాత, మా సవరణ పని చేయడం ఆగిపోయింది:

FOR i IN 1..10000 LOOP
  BEGIN
    INSERT INTO test_table VALUES (1) RETURNING id INTO v_id;
    UPDATE test_table set val = i where id = v_id;
    UPDATE test_table set val = i where id = v_id; -- one more update
    COMMIT;
  END;
END LOOP;

కాబట్టి, ఒక లావాదేవీలో మార్చబడిన ఒరిజినల్ టేబుల్ నుండి డేటా, ఒక లావాదేవీలో కొత్త పట్టికలో కూడా ముగుస్తుందని నిర్ధారించుకోవడం తదుపరి పని.

బ్యాచింగ్ నుండి తిరస్కరణ

మరియు మళ్ళీ మాకు రెండు పరిష్కారాలు ఉన్నాయి. మొదటిది: బ్యాచ్‌లుగా విభజించడాన్ని పూర్తిగా విడిచిపెట్టి, ఒక లావాదేవీలో డేటాను బదిలీ చేద్దాం. ఈ పరిష్కారం యొక్క ప్రయోజనం దాని సరళత - అవసరమైన కోడ్ మార్పులు తక్కువగా ఉన్నాయి (మార్గం ద్వారా, పాత సంస్కరణల్లో pg_reorg సరిగ్గా అలాగే పని చేసింది). కానీ ఒక సమస్య ఉంది - మేము దీర్ఘకాలిక లావాదేవీని సృష్టిస్తున్నాము మరియు ఇది గతంలో చెప్పినట్లుగా, కొత్త ఉబ్బు యొక్క ఆవిర్భావానికి ముప్పు.

రెండవ పరిష్కారం మరింత క్లిష్టంగా ఉంటుంది, కానీ బహుశా మరింత సరైనది: పట్టికకు డేటాను జోడించిన లావాదేవీ ఐడెంటిఫైయర్‌తో లాగ్ పట్టికలో నిలువు వరుసను సృష్టించండి. ఆపై, మేము డేటాను కాపీ చేసినప్పుడు, మేము ఈ లక్షణం ద్వారా సమూహపరచవచ్చు మరియు సంబంధిత మార్పులు కలిసి బదిలీ చేయబడతాయని నిర్ధారించుకోవచ్చు. బ్యాచ్ అనేక లావాదేవీల నుండి (లేదా ఒకటి పెద్దది) ఏర్పడుతుంది మరియు ఈ లావాదేవీలలో ఎంత డేటా మార్చబడిందనే దానిపై ఆధారపడి దాని పరిమాణం మారుతూ ఉంటుంది. వేర్వేరు లావాదేవీల నుండి డేటా యాదృచ్ఛిక క్రమంలో లాగ్ టేబుల్‌లోకి ప్రవేశించినందున, మునుపటిలాగా దాన్ని వరుసగా చదవడం ఇకపై సాధ్యం కాదని గమనించడం ముఖ్యం. tx_id ద్వారా ఫిల్టరింగ్‌తో ప్రతి అభ్యర్థన కోసం seqscan చాలా ఖరీదైనది, ఒక సూచిక అవసరం, కానీ అది అప్‌డేట్ చేసే ఓవర్‌హెడ్ కారణంగా పద్ధతిని నెమ్మదిస్తుంది. సాధారణంగా, ఎప్పటిలాగే, మీరు ఏదో త్యాగం చేయాలి.

కాబట్టి, మేము మొదటి ఎంపికతో ప్రారంభించాలని నిర్ణయించుకున్నాము, ఎందుకంటే ఇది సరళమైనది. మొదట, సుదీర్ఘ లావాదేవీ నిజమైన సమస్య కాదా అని అర్థం చేసుకోవడం అవసరం. పాత పట్టిక నుండి కొత్తదానికి ప్రధాన డేటా బదిలీ కూడా ఒక సుదీర్ఘ లావాదేవీలో జరుగుతుంది కాబట్టి, ప్రశ్న "మేము ఈ లావాదేవీని ఎంత పెంచుతాము?" మొదటి లావాదేవీ వ్యవధి ప్రధానంగా పట్టిక పరిమాణంపై ఆధారపడి ఉంటుంది. డేటా బదిలీ సమయంలో పట్టికలో ఎన్ని మార్పులు పేరుకుపోతాయనే దానిపై కొత్తదాని వ్యవధి ఆధారపడి ఉంటుంది, అనగా. లోడ్ యొక్క తీవ్రతపై. pg_repack రన్ కనిష్ట సేవా లోడ్ సమయంలో సంభవించింది మరియు పట్టిక యొక్క అసలు పరిమాణంతో పోలిస్తే మార్పుల పరిమాణం అసమానంగా తక్కువగా ఉంది. మేము కొత్త లావాదేవీ సమయాన్ని విస్మరించవచ్చని నిర్ణయించుకున్నాము (పోలిక కోసం, సగటున ఇది 1 గంట మరియు 2-3 నిమిషాలు).

ప్రయోగాలు సానుకూలంగా ఉన్నాయి. ప్రొడక్షన్‌లో కూడా ప్రారంభించండి. స్పష్టత కోసం, అమలు చేసిన తర్వాత డేటాబేస్‌లలో ఒకదాని పరిమాణంతో ఇక్కడ ఒక చిత్రం ఉంది:

పోస్ట్‌గ్రెస్: ఉబ్బు, pg_repack మరియు వాయిదా వేసిన పరిమితులు

మేము ఈ పరిష్కారంతో పూర్తిగా సంతృప్తి చెందినందున, మేము రెండవదాన్ని అమలు చేయడానికి ప్రయత్నించలేదు, కానీ మేము దానిని పొడిగింపు డెవలపర్‌లతో చర్చించే అవకాశాన్ని పరిశీలిస్తున్నాము. మా ప్రస్తుత పునర్విమర్శ, దురదృష్టవశాత్తూ, ప్రచురణకు ఇంకా సిద్ధంగా లేదు, ఎందుకంటే మేము ప్రత్యేకమైన వాయిదా వేసిన పరిమితులతో మాత్రమే సమస్యను పరిష్కరించాము మరియు పూర్తి స్థాయి ప్యాచ్ కోసం ఇతర రకాలకు మద్దతుని అందించడం అవసరం. మేము భవిష్యత్తులో దీన్ని చేయగలమని ఆశిస్తున్నాము.

బహుశా మీకు ఒక ప్రశ్న ఉండవచ్చు, మేము pg_repack యొక్క మార్పుతో ఈ కథనంలో ఎందుకు పాల్గొన్నాము మరియు ఉదాహరణకు, దాని అనలాగ్‌లను ఉపయోగించలేదా? ఏదో ఒక సమయంలో మేము దీని గురించి కూడా ఆలోచించాము, కానీ వాయిదా వేసిన పరిమితులు లేకుండా పట్టికలలో ముందుగా ఉపయోగించిన సానుకూల అనుభవం, సమస్య యొక్క సారాంశాన్ని అర్థం చేసుకోవడానికి మరియు దాన్ని పరిష్కరించడానికి ప్రయత్నించడానికి మమ్మల్ని ప్రేరేపించింది. అదనంగా, ఇతర పరిష్కారాలను ఉపయోగించడం కూడా పరీక్షలను నిర్వహించడానికి సమయం అవసరం, కాబట్టి మేము మొదట సమస్యను పరిష్కరించడానికి ప్రయత్నించాలని నిర్ణయించుకున్నాము మరియు సహేతుకమైన సమయంలో దీన్ని చేయలేమని మేము గ్రహించినట్లయితే, మేము అనలాగ్లను చూడటం ప్రారంభిస్తాము. .

కనుగొన్న

మా స్వంత అనుభవం ఆధారంగా మేము ఏమి సిఫార్సు చేయవచ్చు:

  1. మీ ఉబ్బును పర్యవేక్షించండి. పర్యవేక్షణ డేటా ఆధారంగా, ఆటోవాక్యూమ్ ఎంత బాగా కాన్ఫిగర్ చేయబడిందో మీరు అర్థం చేసుకోవచ్చు.
  2. ఉబ్బరం ఆమోదయోగ్యమైన స్థాయిలో ఉంచడానికి AUTOVACUUMని సర్దుబాటు చేయండి.
  3. ఉబ్బరం ఇంకా పెరుగుతూ ఉంటే మరియు మీరు వెలుపలి సాధనాలను ఉపయోగించి దాన్ని అధిగమించలేకపోతే, బాహ్య పొడిగింపులను ఉపయోగించడానికి బయపడకండి. ప్రధాన విషయం ఏమిటంటే ప్రతిదీ బాగా పరీక్షించడం.
  4. మీ అవసరాలకు అనుగుణంగా బాహ్య పరిష్కారాలను సవరించడానికి బయపడకండి - కొన్నిసార్లు ఇది మీ స్వంత కోడ్‌ను మార్చడం కంటే మరింత ప్రభావవంతంగా మరియు సులభంగా ఉంటుంది.

మూలం: www.habr.com

ఒక వ్యాఖ్యను జోడించండి