PostGIS가 올바른 형식의 주소를 지오 코딩하는 데 얼마나 빠릅니까?
PostgreSQL 9.3.7 및 PostGIS 2.1.7을 설치하고 국가 데이터와 모든 주 데이터를로드했지만 지오 코딩이 예상보다 훨씬 느리다는 것을 알았습니다. 기대치를 너무 높게 설정 했습니까? 초당 평균 3 개의 개별 지오 코드가 표시됩니다. 나는 약 5 백만을해야하며 이것을 위해 3 주를 기다리고 싶지 않습니다.
이것은 거대한 R 행렬을 처리하기위한 가상 머신 이며이 데이터베이스를 측면에 설치 했으므로 구성이 약간 까다로워 보일 수 있습니다. VM 구성의 주요 변경이 도움이 될 경우 구성을 변경할 수 있습니다.
하드웨어 사양
메모리 : 65GB 프로세서 : 6
lscpu
은 다음을 제공합니다.
# lscpu
Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Byte Order: Little Endian
CPU(s): 6
On-line CPU(s) list: 0-5
Thread(s) per core: 1
Core(s) per socket: 1
Socket(s): 6
NUMA node(s): 1
Vendor ID: GenuineIntel
CPU family: 6
Model: 58
Stepping: 0
CPU MHz: 2400.000
BogoMIPS: 4800.00
Hypervisor vendor: VMware
Virtualization type: full
L1d cache: 32K
L1i cache: 32K
L2 cache: 256K
L3 cache: 30720K
NUMA node0 CPU(s): 0-5
OS는 centos입니다 uname -rv
.
# uname -rv
2.6.32-504.16.2.el6.x86_64 #1 SMP Wed Apr 22 06:48:29 UTC 2015
PostgreSQL 설정
> select version()
"PostgreSQL 9.3.7 on x86_64-unknown-linux-gnu, compiled by gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-11), 64-bit"
> select PostGIS_Full_version()
POSTGIS="2.1.7 r13414" GEOS="3.4.2-CAPI-1.8.2 r3921" PROJ="Rel. 4.8.0, 6 March 2012" GDAL="GDAL 1.9.2, released 2012/10/08" LIBXML="2.7.6" LIBJSON="UNKNOWN" TOPOLOGY RASTER"
이러한 유형의 쿼리에 대한 이전 제안 shared_buffers
에 따라 postgresql.conf
파일에서 사용 가능한 RAM의 약 1/4로, 유효 캐시 크기는 RAM의 1/2로 상향 조정 했습니다 .
shared_buffers = 16096MB
effective_cache_size = 31765MB
나는 installed_missing_indexes()
(일부 테이블에 중복 삽입을 해결 한 후) 오류가 없었습니다.
지오 코딩 SQL 예제 # 1 (일괄 처리) ~ 평균 시간은 2.8 / 초입니다
http://postgis.net/docs/Geocode.html 의 예제를 따라 지오 코딩 할 주소가 포함 된 테이블을 만든 다음 SQL을 수행합니다 UPDATE
.
UPDATE addresses_to_geocode
SET (rating, longitude, latitude,geo)
= ( COALESCE((g.geom).rating,-1),
ST_X((g.geom).geomout)::numeric(8,5),
ST_Y((g.geom).geomout)::numeric(8,5),
geo )
FROM (SELECT "PatientId" as PatientId
FROM addresses_to_geocode
WHERE "rating" IS NULL ORDER BY PatientId LIMIT 1000) As a
LEFT JOIN (SELECT "PatientId" as PatientId, (geocode("Address",1)) As geom
FROM addresses_to_geocode As ag
WHERE ag.rating IS NULL ORDER BY PatientId LIMIT 1000) As g ON a.PatientId = g.PatientId
WHERE a.PatientId = addresses_to_geocode."PatientId";
1000 이상의 배치 크기를 사용하고 있으며 337.70 초 만에 반환됩니다. 작은 배치의 경우 조금 느립니다.
지오 코딩 SQL 예제 # 2 (행별) ~ 평균 시간은 1.2 / 초
다음과 같은 문장으로 지오 코드를 한 번에 하나씩 수행하여 내 주소를 파헤 치면 (아래 예제는 4.14 초가 걸렸습니다),
SELECT g.rating, ST_X(g.geomout) As lon, ST_Y(g.geomout) As lat,
(addy).address As stno, (addy).streetname As street,
(addy).streettypeabbrev As styp, (addy).location As city,
(addy).stateabbrev As st,(addy).zip
FROM geocode('6433 DROMOLAND Cir NW, MASSILLON, OH 44646',1) As g;
그것은 조금 느리지 만 (레코드 당 2.5 배) 쿼리 시간의 분포를 볼 수 있으며이를 가장 느리게하는 소수의 긴 쿼리임을 알 수 있습니다 (5 백만의 처음 2600 만 조회 시간이 있음). 즉, 상위 10 %는 평균 약 100ms, 하위 10 % 평균 3.69 초, 평균은 754ms, 중앙값은 340ms입니다.
# Just some interaction with the data in R
> range(lookupTimes[1:2600])
[1] 0.00 11.54
> median(lookupTimes[1:2600])
[1] 0.34
> mean(lookupTimes[1:2600])
[1] 0.7541808
> mean(sort(lookupTimes[1:2600])[1:260])
[1] 0.09984615
> mean(sort(lookupTimes[1:2600],decreasing=TRUE)[1:260])
[1] 3.691269
> hist(lookupTimes[1:2600]
다른 생각들
성능이 크게 향상되지 않으면 적어도 지오 코딩 시간을 예측하는 것에 대한 교육적인 추측을 할 수 있다고 생각했지만 느린 주소가 왜 그렇게 오래 걸리는지는 분명하지 않습니다. geocode()
함수가 가져 오기 전에 형식이 올바른지 확인하기 위해 사용자 지정 정규화 단계를 통해 원래 주소를 실행하고 있습니다.
sql=paste0("select pprint_addy(normalize_address('",myAddress,"'))")
여기서 myAddress
A는 [Address], [City], [ST] [Zip]
비 PostgreSQL을 데이터베이스에서 사용자 주소 테이블에서 컴파일 된 문자열.
pagc_normalize_address
확장 프로그램 을 설치하려고했지만 실패 했지만 이것이 내가 원하는 종류의 개선을 가져올 것이라는 것은 확실하지 않습니다.
제안에 따라 모니터링 정보를 추가하도록 편집
공연
하나의 CPU가 페깅됩니다. [편집, 쿼리 당 하나의 프로세서 만 있으므로 5 개의 사용되지 않은 CPU가 있습니다]
top - 14:10:26 up 1 day, 3:11, 4 users, load average: 1.02, 1.01, 0.93
Tasks: 219 total, 2 running, 217 sleeping, 0 stopped, 0 zombie
Cpu(s): 15.4%us, 1.5%sy, 0.0%ni, 83.1%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Mem: 65056588k total, 64613476k used, 443112k free, 97096k buffers
Swap: 262139900k total, 77164k used, 262062736k free, 62745284k cached
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
3130 postgres 20 0 16.3g 8.8g 8.7g R 99.7 14.2 170:14.06 postmaster
11139 aolsson 20 0 15140 1316 932 R 0.3 0.0 0:07.78 top
11675 aolsson 20 0 135m 1836 1504 S 0.3 0.0 0:00.01 wget
1 root 20 0 19364 1064 884 S 0.0 0.0 0:01.84 init
2 root 20 0 0 0 0 S 0.0 0.0 0:00.06 kthreadd
하나의 프로세스가 100 %로 페그되는 동안 데이터 파티션의 디스크 활동 샘플 : [편집 :이 쿼리에서 사용중인 프로세서는 하나만]
# dstat -tdD dm-3 1
----system---- --dsk/dm-3-
date/time | read writ
12-06 14:06:36|1818k 3632k
12-06 14:06:37| 0 0
12-06 14:06:38| 0 0
12-06 14:06:39| 0 0
12-06 14:06:40| 0 40k
12-06 14:06:41| 0 0
12-06 14:06:42| 0 0
12-06 14:06:43| 0 8192B
12-06 14:06:44| 0 8192B
12-06 14:06:45| 120k 60k
12-06 14:06:46| 0 0
12-06 14:06:47| 0 0
12-06 14:06:48| 0 0
12-06 14:06:49| 0 0
12-06 14:06:50| 0 28k
12-06 14:06:51| 0 96k
12-06 14:06:52| 0 0
12-06 14:06:53| 0 0
12-06 14:06:54| 0 0 ^C
해당 SQL 분석
이것은 EXPLAIN ANALYZE
해당 쿼리 에서 온 것입니다.
"Update on addresses_to_geocode (cost=1.30..8390.04 rows=1000 width=272) (actual time=363608.219..363608.219 rows=0 loops=1)"
" -> Merge Left Join (cost=1.30..8390.04 rows=1000 width=272) (actual time=110.934..324648.385 rows=1000 loops=1)"
" Merge Cond: (a.patientid = g.patientid)"
" -> Nested Loop (cost=0.86..8336.82 rows=1000 width=184) (actual time=10.676..34.241 rows=1000 loops=1)"
" -> Subquery Scan on a (cost=0.43..54.32 rows=1000 width=32) (actual time=10.664..18.779 rows=1000 loops=1)"
" -> Limit (cost=0.43..44.32 rows=1000 width=4) (actual time=10.658..17.478 rows=1000 loops=1)"
" -> Index Scan using "addresses_to_geocode_PatientId_idx" on addresses_to_geocode addresses_to_geocode_1 (cost=0.43..195279.22 rows=4449758 width=4) (actual time=10.657..17.021 rows=1000 loops=1)"
" Filter: (rating IS NULL)"
" Rows Removed by Filter: 24110"
" -> Index Scan using "addresses_to_geocode_PatientId_idx" on addresses_to_geocode (cost=0.43..8.27 rows=1 width=152) (actual time=0.010..0.013 rows=1 loops=1000)"
" Index Cond: ("PatientId" = a.patientid)"
" -> Materialize (cost=0.43..18.22 rows=1000 width=96) (actual time=100.233..324594.558 rows=943 loops=1)"
" -> Subquery Scan on g (cost=0.43..15.72 rows=1000 width=96) (actual time=100.230..324593.435 rows=943 loops=1)"
" -> Limit (cost=0.43..5.72 rows=1000 width=42) (actual time=100.225..324591.603 rows=943 loops=1)"
" -> Index Scan using "addresses_to_geocode_PatientId_idx" on addresses_to_geocode ag (cost=0.43..23534259.93 rows=4449758000 width=42) (actual time=100.225..324591.146 rows=943 loops=1)"
" Filter: (rating IS NULL)"
" Rows Removed by Filter: 24110"
"Total runtime: 363608.316 ms"
http://explain.depesz.com/s/vogS 에서 더 나은 고장보기