अनुक्रमण का मान
PostgreSQL एक साधारण रैखिक दूरी ऑपरेटर प्रदान करता है <->
(रैखिक दूरी)। हम इसका उपयोग किसी दिए गए स्थान के निकटतम बिंदुओं को खोजने के लिए करेंगे।
PostgreSQL एक साधारण रैखिक दूरी ऑपरेटर डेटा प्रदान करता है, और कोई अनुकूलन नहीं करता है और कोई अनुक्रमणिका नहीं है, हम निम्नलिखित निष्पादन योजना देखते हैं:
time psql -qtAc "
EXPLAIN (ANALYZE ON, BUFFERS ON)
SELECT name, location
FROM geonames
ORDER BY location <-> '(29.9691,-95.6972)'
LIMIT 5;
" <-- closing quote
QUERY PLAN
-----------------------------------------------------------------------------------------------------------
Limit (cost=418749.15..418749.73 rows=5 width=38)
(actual time=2553.970..2555.673 rows=5 loops=1)
Buffers: shared hit=100 read=272836
-> Gather Merge (cost=418749.15..1580358.21 rows=9955954 width=38)
(actual time=2553.969..2555.669 rows=5 loops=1)
Workers Planned: 2
Workers Launched: 2
Buffers: shared hit=100 read=272836
-> Sort (cost=417749.12..430194.06 rows=4977977 width=38)
(actual time=2548.220..2548.221 rows=4 loops=3)
Sort Key: ((location <-> '(29.9691,-95.6972)'::point))
Sort Method: top-N heapsort Memory: 25kB
Worker 0: Sort Method: top-N heapsort Memory: 26kB
Worker 1: Sort Method: top-N heapsort Memory: 25kB
Buffers: shared hit=100 read=272836
-> Parallel Seq Scan on geonames (cost=0.00..335066.71 rows=4977977 width=38)
(actual time=0.040..1637.884 rows=3982382 loops=3)
Buffers: shared hit=6 read=272836
Planning Time: 0.493 ms
Execution Time: 2555.737 ms
real 0m2.595s
user 0m0.011s
sys 0m0.015s
और यहां परिणाम हैं:(सभी अनुरोधों के लिए समान परिणाम, इसलिए हम उन्हें बाद में छोड़ देंगे।)
नाम | स्थान |
---|---|
सरू | (29.96911,-95.69717) |
साइप्रेस पॉइंट बैपटिस्ट चर्च | (29.9732,-95.6873) |
सरू डाकघर | (29.9743,-95.67953) |
हॉट वेल | (29.95689,-95.68189) |
ड्राई क्रीक एयरपोर्ट | (29.98571,-95.68597) |
तो, 418749.73 ऑप्टिमाइज़र लागत को हराने के लिए है, और उस क्वेरी को निष्पादित करने में ढाई सेकंड (2555.673) का समय लगा। यह वास्तव में एक बहुत अच्छा परिणाम है, 11 मिलियन पंक्ति तालिका के विरुद्ध PostgreSQL का उपयोग बिना किसी अनुकूलन के। यही कारण है कि हमने एक बड़े डेटा सेट का चयन किया, क्योंकि 10 मिलियन से कम पंक्तियों के खिलाफ इंडेक्स का उपयोग करने में बहुत कम अंतर होगा। समानांतर अनुक्रमिक स्कैन शानदार हैं, लेकिन यह एक और लेख है।
जिस्ट इंडेक्स जोड़ना
हम जीआईएसटी इंडेक्स जोड़कर अनुकूलन प्रक्रिया शुरू करते हैं। क्योंकि हमारी उदाहरण क्वेरी में एक
. हैLIMIT
5 वस्तुओं का खंड, हमारे पास बहुत अधिक चयनात्मकता है। यह योजनाकार को एक इंडेक्स का उपयोग करने के लिए प्रोत्साहित करेगा, इसलिए हम एक ऐसा प्रदान करेंगे जो ज्यामिति डेटा के साथ काफी अच्छी तरह से काम करता है।
time psql -qtAc "CREATE INDEX idx_gist_geonames_location ON geonames USING gist(location);"
सूचकांक बनाने के कार्य में थोड़ा खर्च होता है।
CREATE INDEX
real 3m1.988s
user 0m0.011s
sys 0m0.014s
और फिर वही क्वेरी फिर से चलाएँ।
time psql -qtAc "
EXPLAIN (ANALYZE ON, BUFFERS ON)
SELECT name, location
FROM geonames
ORDER BY location <-> '(29.9691,-95.6972)'
LIMIT 5;
"
QUERY PLAN
----------------------------------------------------------------------------------
Limit (cost=0.42..1.16 rows=5 width=38) (actual time=0.797..0.881 rows=5 loops=1)
Buffers: shared hit=5 read=15
-> Index Scan using idx_gist_geonames_location on geonames
(cost=0.42..1773715.32 rows=11947145 width=38)
(actual time=0.796..0.879 rows=5 loops=1)
Order By: (location <-> '(29.9691,-95.6972)'::point)
Buffers: shared hit=5 read=15
Planning Time: 0.768 ms
Execution Time: 0.939 ms
real 0m0.033s
user 0m0.011s
sys 0m0.013s
इस मामले में, हम कुछ बहुत नाटकीय सुधार देखते हैं। क्वेरी की अनुमानित लागत केवल 1.16 है! इसकी तुलना 418749.73 पर गैर-अनुकूलित क्वेरी की मूल लागत से करें। लिया गया वास्तविक समय .939 मिलीसेकंड (एक मिलीसेकंड का नौ दसवां हिस्सा) था, जो मूल क्वेरी के 2.5 सेकंड की तुलना में है। इस परिणाम ने योजना बनाने में कम समय लिया, नाटकीय रूप से बेहतर अनुमान प्राप्त किया, और परिमाण कम रनटाइम के लगभग 3 ऑर्डर लिए।
देखते हैं कि क्या हम बेहतर कर सकते हैं।
SP-GiST अनुक्रमणिका जोड़ना
time psql -qtAc "CREATE INDEX idx_spgist_geonames_location ON geonames USING spgist(location);"
CREATE INDEX
real 1m25.205s
user 0m0.010s
sys 0m0.015s
और फिर हम वही क्वेरी फिर से चलाते हैं।
time psql -qtAc "
EXPLAIN (ANALYZE ON, BUFFERS ON)
SELECT name, location
FROM geonames
ORDER BY location <-> '(29.9691,-95.6972)'
LIMIT 5;
"
QUERY PLAN
-----------------------------------------------------------------------------------
Limit (cost=0.42..1.09 rows=5 width=38) (actual time=0.066..0.323 rows=5 loops=1)
Buffers: shared hit=47
-> Index Scan using idx_spgist_geonames_location on geonames
(cost=0.42..1598071.32 rows=11947145 width=38)
(actual time=0.065..0.320 rows=5 loops=1)
Order By: (location <-> '(29.9691,-95.6972)'::point)
Buffers: shared hit=47
Planning Time: 0.122 ms
Execution Time: 0.358 ms
(7 rows)
real 0m0.040s
user 0m0.011s
sys 0m0.015s
बहुत खूब! अब SP-GiST इंडेक्स का उपयोग करते हुए, क्वेरी की लागत केवल 1.09 है, और इसे .358 मिलीसेकंड (मिलीसेकंड का एक तिहाई) में निष्पादित किया जाता है।
आइए खुद इंडेक्स के बारे में कुछ चीजों की जांच करें, और देखें कि वे डिस्क पर एक दूसरे के साथ कैसे ढेर हो जाते हैं।
सूचकांक तुलना
indexname | निर्माण समय | अनुमान | क्वेरी का समय | अनुक्रमित करें | योजना समय |
---|---|---|---|---|---|
अनइंडेक्स्ड | 0S | 418749.73 | 2555.673 | 0 | .493 |
idx_gist_geonames_location | 3M 1S | 1.16 | .939 ms | 868 एमबी | .786 |
idx_spgist_geonames_location | 1M 25S | 1.09 | .358 ms | 523 MB | .122 |
निष्कर्ष
इसलिए, हम देखते हैं कि एसपी-जीआईएसटी निष्पादन में जीआईएसटी की गति से दोगुना है, योजना के लिए 8 गुना तेज है, और डिस्क पर आकार का लगभग 60% है। और (इस लेख के लिए प्रासंगिक) यह पोस्टग्रेएसक्यूएल 12 के रूप में केएनएन इंडेक्स खोज का भी समर्थन करता है। इस प्रकार के ऑपरेशन के लिए, हमारे पास एक स्पष्ट विजेता है।
परिशिष्ट
डेटा सेट करना
इस लेख के लिए, हम GeoNames Gazetteer द्वारा प्रदान किए गए डेटा का उपयोग करने जा रहे हैं।
यह कार्य Creative Commons Attribution 4.0 लाइसेंस के तहत लाइसेंस प्राप्त है
डेटा बिना वारंटी या किसी भी प्रतिनिधित्व के "जैसा है" प्रदान किया जाता है सटीकता, समयबद्धता या पूर्णता।
संरचना बनाएं
हम एक कार्यशील निर्देशिका और थोड़ा सा ईटीएल बनाकर प्रक्रिया शुरू करते हैं।
# change to our home directory
cd
mkdir spgist
cd spgist
# get the base data.
# This file is 350MB. It will unpack to 1.5GB
# It will expand to 2GB in PostgreSQL,
# and then you will still need some room for indexes
# All together, you will need about
# 3GB of space for this exercise
# for about 12M rows of data.
psql -qtAc "
CREATE TABLE IF NOT EXISTS geonames (
geonameid integer primary key
,name text
,asciiname text
,alternatenames text
,latitude numeric(13,5)
,longitude numeric(13,5)
,feature_class text
,feature_code text
,country text
,cc2 text
,admin1 text
,admin2 bigint
,admin3 bigint
,admin4 bigint
,population bigint
,elevation bigint
,dem bigint
,timezone text
,modification date );
COMMENT ON COLUMN geonames.geonameid
IS ' integer id of record in geonames database';
COMMENT ON COLUMN geonames.name
IS ' name of geographical point (utf8) varchar(200)';
COMMENT ON COLUMN geonames.asciiname
IS ' name of geographical point in plain ascii characters, varchar(200)';
COMMENT ON COLUMN geonames.alternatenames
IS ' alternatenames, comma separated, ascii names automatically transliterated,
convenience attribute from alternatename table, varchar(10000)';
COMMENT ON COLUMN geonames.latitude
IS ' latitude in decimal degrees (wgs84)';
COMMENT ON COLUMN geonames.longitude
IS ' longitude in decimal degrees (wgs84)';
COMMENT ON COLUMN geonames.feature_class
IS ' http://www.geonames.org/export/codes.html, char(1)';
COMMENT ON COLUMN geonames.feature_code
IS ' http://www.geonames.org/export/codes.html, varchar(10)';
COMMENT ON COLUMN geonames.country
IS ' ISO-3166 2-letter country code, 2 characters';
COMMENT ON COLUMN geonames.cc2
IS ' alternate country codes, comma separated, ISO-3166 2-letter country code,
200 characters';
COMMENT ON COLUMN geonames.admin1
IS ' fipscode (subject to change to iso code), see exceptions below,
see file admin1Codes.txt for display names of this code; varchar(20)';
COMMENT ON COLUMN geonames.admin2
IS ' code for the second administrative division, a county in the US,
see file admin2Codes.txt; varchar(80) ';
COMMENT ON COLUMN geonames.admin3
IS ' code for third level administrative division, varchar(20)';
COMMENT ON COLUMN geonames.admin4
IS ' code for fourth level administrative division, varchar(20)';
COMMENT ON COLUMN geonames.population
IS ' bigint (8 byte int) ';
COMMENT ON COLUMN geonames.elevation
IS ' in meters, integer';
COMMENT ON COLUMN geonames.dem
IS ' digital elevation model, srtm3 or gtopo30, average elevation of 3''x3''
(ca 90mx90m) or 30''x30'' (ca 900mx900m) area in meters, integer.
srtm processed by cgiar/ciat.';
COMMENT ON COLUMN geonames.timezone
IS ' the iana timezone id (see file timeZone.txt) varchar(40)';
COMMENT ON COLUMN geonames.modification
IS ' date of last modification in yyyy-MM-dd format';
" #<-- Don't forget the closing quote
ETL
wget http://download.geonames.org/export/dump/allCountries.zip
unzip allCountries.zip
# do this, and go get a coffee. This took nearly an hour
# there will be a few lines that fail, they don't really matter much
IFS=$'\n'
for line in $(<allCountries.txt)
do
echo -n "$line" |
psql -qtAc
"COPY geonames FROM STDIN WITH CSV DELIMITER E'\t';"
2> errors.txt
done
क्लीन अप और सेट अप करें
बाकी सब कुछ हम psql के अंदर से करते हैं:
psql
-- This command requires the installation
-- of postgis2 from your OS package manager.
-- For OS/X that was `port install postgresql12-postgis2`
-- it will be something similar on most platforms.
-- (e.g. apt-get install postgresql12-postgis2,
-- yum -y install postgresql12-postgis2, etc.)
CREATE EXTENSION postgis;
CREATE EXTENSION postgis_topology;
ALTER TABLE geonames ADD COLUMN location point;
-- Go get another cup of coffee, this is going to rewrite the entire table with the new geo column.
UPDATE geonames SET location = ('(' || latitude || ', ' || longitude || ')')::point;
DELETE FROM geonames WHERE latitude IS NULL or longitude IS NULL;
-- DELETE 32 -- In my case, this ETL anomoly was too small
-- to bother fixing the records
-- Bloat removal from the update and delete operations
CLUSTER geonames USING geonames_pkey;