Seberapa cepat saya harus mengharapkan PostGIS untuk melakukan geocode pada alamat yang diformat dengan baik?
Saya telah menginstal PostgreSQL 9.3.7 dan PostGIS 2.1.7, memuat data nation dan semua data status tetapi telah menemukan geocoding jauh lebih lambat dari yang saya perkirakan. Apakah harapan saya terlalu tinggi? Saya mendapatkan rata-rata 3 geocode individu per detik. Saya perlu melakukan sekitar 5 juta dan saya tidak ingin menunggu tiga minggu untuk ini.
Ini adalah mesin virtual untuk memproses matriks R raksasa dan saya menginstal database ini di samping sehingga konfigurasi mungkin terlihat sedikit konyol. Jika perubahan besar dalam konfigurasi VM akan membantu, saya dapat mengubah konfigurasi.
Spesifikasi perangkat keras
Memori: 65GB prosesor: 6
lscpu
memberi saya ini:
# 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 adalah centos, uname -rv
berikan ini:
# uname -rv
2.6.32-504.16.2.el6.x86_64 #1 SMP Wed Apr 22 06:48:29 UTC 2015
Konfigurasi 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"
Berdasarkan saran sebelumnya untuk jenis query, saya menaikkan shared_buffers
dalam postgresql.conf
file sekitar 1/4 dari RAM yang tersedia dan ukuran cache yang efektif untuk 1/2 RAM:
shared_buffers = 16096MB
effective_cache_size = 31765MB
Saya punya installed_missing_indexes()
dan (setelah menyelesaikan duplikat sisipan ke beberapa tabel) tidak memiliki kesalahan.
Contoh Geocoding SQL # 1 (batch) ~ waktu rata-rata adalah 2,8 / detik
Saya mengikuti contoh dari http://postgis.net/docs/Geocode.html , yang membuat saya membuat tabel yang berisi alamat ke geocode, dan kemudian melakukan 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";
Saya menggunakan ukuran batch 1000 di atas dan kembali dalam 337,70 detik. Ini sedikit lebih lambat untuk batch yang lebih kecil.
Contoh geocoding SQL # 2 (baris demi baris) ~ waktu rata-rata adalah 1,2 / detik
Ketika saya menggali alamat saya dengan melakukan geocode satu per satu dengan pernyataan yang terlihat seperti ini (btw, contoh di bawah ini butuh 4,14 detik),
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;
ini sedikit lebih lambat (2,5x per record) tapi saya bisa melihat distribusi waktu permintaan dan melihat bahwa itu adalah minoritas dari pertanyaan panjang yang paling memperlambat ini (hanya 2600 dari 5 juta pertama yang memiliki waktu pencarian). Artinya, 10% teratas mengambil rata-rata sekitar 100 ms, 10% terbawah rata-rata 3,69 detik, sedangkan rata-rata adalah 754 ms dan median adalah 340 ms.
# 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]
Pikiran lain
Jika saya tidak bisa mendapatkan urutan peningkatan kinerja besar, saya pikir saya setidaknya bisa membuat perkiraan berpendidikan tentang memprediksi waktu geocode lambat tetapi tidak jelas bagi saya mengapa alamat yang lebih lambat tampaknya memakan waktu lebih lama. Saya menjalankan alamat asli melalui langkah normalisasi khusus untuk memastikan itu diformat dengan baik sebelum geocode()
fungsi mendapatkannya:
sql=paste0("select pprint_addy(normalize_address('",myAddress,"'))")
di mana myAddress
adalah [Address], [City], [ST] [Zip]
string yang disusun dari tabel alamat pengguna dari database non-postgresql.
Saya mencoba (gagal) untuk menginstal pagc_normalize_address
ekstensi tetapi tidak jelas bahwa ini akan membawa jenis perbaikan yang saya cari.
Diedit untuk menambahkan informasi pemantauan sesuai saran
Performa
Satu CPU dipatok: [edit, hanya satu prosesor per permintaan, jadi saya punya 5 CPU yang tidak digunakan]
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
Contoh aktivitas disk pada partisi data sementara satu proc dipatok pada 100%: [edit: hanya satu prosesor yang digunakan oleh kueri ini]
# 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
Analisis SQL itu
Ini dari EXPLAIN ANALYZE
pada permintaan itu:
"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"
Lihat rincian yang lebih baik di http://explain.depesz.com/s/vogS
Jawaban:
Saya telah menghabiskan banyak waktu bereksperimen dengan ini, saya pikir lebih baik memposting secara terpisah karena mereka dari sudut yang berbeda.
Ini benar-benar topik yang kompleks, lihat lebih detail di posting blog saya tentang pengaturan server geocoding dan skrip yang saya gunakan ., Di sini hanya beberapa ringkasan singkat:
Server dengan hanya 2 data Negara selalu lebih cepat dari server yang dimuat dengan semua 50 status data.
Saya memverifikasi ini dengan PC rumah saya di waktu yang berbeda dan dua server Amazon AWS berbeda.
Server tingkat bebas AWS saya dengan 2 status data hanya memiliki RAM 1G, tetapi memiliki kinerja 43 ~ 59 ms yang konsisten untuk data dengan 1000 catatan dan 45.000 catatan.
Saya menggunakan prosedur penyetelan yang persis sama untuk server AWS 8G RAM dengan semua status dimuat, skrip dan data yang sama persis, dan kinerjanya turun menjadi 80 ~ 105 ms.
Teori saya adalah bahwa ketika geocoder tidak dapat mencocokkan alamat dengan tepat, geocoder mulai memperluas jangkauan pencarian dan mengabaikan beberapa bagian, seperti kode pos atau kota. Itu sebabnya dokumen geocode membanggakan bahwa ia dapat mengkolonisasi ulang alamat dengan kode pos yang salah, meskipun butuh 3000 ms.
Dengan hanya 2 status yang dimuat, server akan membutuhkan waktu yang jauh lebih sedikit dalam pencarian tanpa hasil atau kecocokan dengan skor yang sangat rendah, karena hanya dapat mencari di 2 negara.
Saya mencoba membatasi ini dengan menetapkan
restrict_region
parameter ke status multipoligon dalam fungsi geocode, berharap itu akan menghindari pencarian sia-sia karena saya cukup yakin sebagian besar alamat memiliki keadaan yang benar. Bandingkan dua versi ini:Satu-satunya perbedaan yang dibuat oleh versi kedua adalah bahwa biasanya jika saya menjalankan query yang sama segera lagi itu akan jauh lebih cepat karena data terkait di-cache, tetapi versi kedua menonaktifkan efek ini.
Jadi
restrict_region
tidak berfungsi seperti yang saya inginkan, mungkin itu hanya digunakan untuk menyaring beberapa hasil klik, bukan untuk membatasi rentang pencarian.Anda dapat mengatur sedikit postgre conf Anda sedikit.
Tersangka yang biasa menginstal indeks yang hilang, analisis vakum tidak membuat perbedaan bagi saya, karena skrip pengunduhan telah melakukan pemeliharaan yang diperlukan, kecuali jika Anda mengacaukannya.
Namun pengaturan postgre conf menurut posting ini memang membantu. Server skala penuh saya dengan 50 negara bagian memiliki 320 ms dengan konfigurasi default untuk beberapa data berbentuk lebih buruk, itu ditingkatkan menjadi 185 ms dengan 2G shared_buffer, 5G cache, dan pergi ke 100 ms lebih lanjut dengan sebagian besar pengaturan disetel sesuai dengan posting itu.
Ini lebih relevan untuk postgis dan pengaturannya tampaknya serupa.
Ukuran batch dari setiap commit tidak terlalu menjadi masalah bagi kasus saya. Dokumentasi geocode menggunakan ukuran batch 3. Saya melakukan percobaan nilai dari 1, 3, 5 hingga 10. Saya tidak menemukan perbedaan yang signifikan dengan ini. Dengan ukuran batch yang lebih kecil Anda membuat lebih banyak komitmen dan pembaruan, tetapi saya pikir leher botol yang sebenarnya tidak ada di sini. Sebenarnya saya menggunakan ukuran batch 1 sekarang. Karena selalu ada beberapa alamat yang tidak diharapkan yang terbentuk dengan buruk akan menyebabkan pengecualian, saya akan mengatur seluruh kumpulan dengan kesalahan sebagai diabaikan dan melanjutkan untuk baris yang tersisa. Dengan ukuran batch 1, saya tidak perlu memproses tabel untuk kedua kalinya untuk melakukan geocode pada catatan yang mungkin baik dalam batch yang ditandai sebagai diabaikan.
Tentu saja ini tergantung pada cara kerja skrip batch Anda. Saya akan memposting skrip saya dengan lebih detail nanti.
Anda dapat mencoba menggunakan alamat normalisasi untuk memfilter alamat yang buruk jika sesuai dengan penggunaan Anda. Saya melihat seseorang menyebutkan ini di suatu tempat, tetapi saya tidak yakin bagaimana ini bekerja karena fungsi normalisasi hanya berfungsi dalam format, ia tidak dapat memberi tahu Anda alamat mana yang tidak benar.
Kemudian saya menyadari bahwa jika alamatnya jelas-jelas buruk dan Anda ingin melewatkannya, ini bisa membantu. Misalnya saya punya banyak alamat yang hilang nama jalan atau bahkan nama jalan. Normalisasi semua alamat terlebih dahulu akan relatif cepat, maka Anda dapat memfilter alamat buruk yang jelas untuk Anda kemudian melewati mereka. Namun ini tidak sesuai dengan penggunaan saya karena alamat tanpa nomor jalan atau bahkan nama jalan masih dapat dipetakan ke jalan atau kota, dan informasi itu masih berguna bagi saya.
Dan sebagian besar alamat yang tidak dapat di-geocode dalam kasus saya sebenarnya memiliki semua bidang, hanya saja tidak ada kecocokan dalam basis data. Anda tidak dapat memfilter alamat ini hanya dengan menormalkannya.
Sunting Untuk lebih jelasnya, lihat posting blog saya tentang pengaturan server geocoding dan skrip yang saya gunakan .
EDIT 2 Saya telah menyelesaikan geocoding 2 juta alamat dan melakukan banyak pembersihan pada alamat berdasarkan hasil geocoding. Dengan input yang dibersihkan dengan lebih baik, pekerjaan batch berikutnya berjalan jauh lebih cepat. Dengan bersih, maksud saya beberapa alamat jelas salah dan harus dihapus, atau memiliki konten yang tidak terduga untuk geocoder menyebabkan masalah pada geocoding. Teori saya adalah: Menghapus alamat yang buruk dapat menghindari mengacaukan cache, yang meningkatkan kinerja alamat yang baik secara signifikan.
Saya memisahkan input berdasarkan status untuk memastikan setiap pekerjaan dapat memiliki semua data yang diperlukan untuk geocoding di-cache dalam RAM. Namun setiap alamat yang buruk dalam pekerjaan membuat geocoder untuk mencari di lebih banyak negara, yang dapat mengacaukan cache.
sumber
Menurut utas diskusi ini , Anda seharusnya menggunakan prosedur normalisasi yang sama untuk memproses data Tiger dan alamat input Anda. Karena data Tiger telah diproses dengan normalizer bawaan, lebih baik hanya menggunakan normalizer bawaan. Bahkan jika Anda berhasil membuat pagc_normalizer, itu mungkin tidak membantu Anda jika Anda tidak menggunakannya untuk memperbarui data Tiger.
Yang sedang berkata, saya pikir geocode () akan memanggil normalizer tetap jadi menormalkan alamat sebelum geocoding mungkin tidak benar-benar berguna. Salah satu kemungkinan penggunaan normalizer dapat membandingkan alamat yang dinormalisasi dan alamat yang dikembalikan oleh geocode (). Dengan keduanya dinormalisasi, akan lebih mudah untuk menemukan hasil geocoding yang salah.
Jika Anda dapat memfilter alamat buruk dari geocode oleh normalizer, itu akan sangat membantu. Namun saya tidak melihat normalizer memiliki sesuatu seperti skor pertandingan atau peringkat.
Utas diskusi yang sama juga menyebutkan sakelar debug
geocode_address
untuk menampilkan informasi lebih lanjut. Nodegeocode_address
perlu input alamat yang dinormalisasi.Geocoder cepat untuk pencocokan tepat tetapi membutuhkan lebih banyak waktu untuk kasus-kasus sulit. Saya menemukan ada parameter
restrict_region
dan berpikir mungkin itu akan membatasi pencarian sia-sia jika saya menetapkan batas sebagai negara karena saya cukup yakin di negara mana ia akan masuk. Ternyata pengaturan ke keadaan yang salah tidak menghentikan geocode untuk mendapatkan alamat yang benar, meskipun perlu waktu.Jadi mungkin geocoder akan mencari di semua tempat yang memungkinkan jika pencarian tepat pertama tidak cocok. Ini membuatnya dapat memproses input dengan beberapa kesalahan, tetapi juga membuat beberapa pencarian sangat lambat.
Saya pikir itu baik untuk layanan interaktif untuk menerima input dengan kesalahan, tetapi kadang-kadang kita mungkin ingin menyerah pada set kecil alamat yang salah untuk memiliki kinerja yang lebih baik dalam geocoding batch.
sumber
restrict_region
waktu ketika Anda mengatur negara yang benar? Juga, dari utas postgis-pengguna yang Anda tautkan di atas, mereka menyebutkan secara khusus mengalami masalah dengan alamat seperti1020 Highway 20
yang saya temui juga.Saya akan memposting jawaban ini tetapi mudah-mudahan kontributor lain akan membantu menjabarkan berikut ini, yang saya pikir akan memberikan gambaran yang lebih masuk akal:
Sekarang jawaban saya, yang hanya sebuah anekdot:
Yang terbaik yang saya dapatkan (berdasarkan satu koneksi) adalah rata-rata 208 ms per
geocode
. Ini diukur dengan memilih alamat secara acak dari dataset saya, yang meluas di seluruh AS. Ini mencakup beberapa data kotor tetapi yang paling lama berjalangeocode
tampaknya tidak buruk dengan cara yang jelas.Intinya adalah bahwa saya tampaknya terikat CPU dan satu permintaan tunggal terikat pada satu prosesor. Saya dapat memparalelkan ini dengan membuat beberapa koneksi berjalan dengan
UPDATE
terjadi pada segmen komplementer dariaddresses_to_geocode
tabel secara teori. Sementara itu, sayageocode
harus mengambil rata-rata 208 ms pada set data nasional. Distribusi miring baik dalam hal di mana sebagian besar alamat saya berada dan dalam hal berapa lama mereka mengambil (misalnya, lihat histogram di atas) dan tabel di bawah ini.Pendekatan terbaik saya sejauh ini adalah melakukannya dalam batch 10.000, dengan beberapa peningkatan yang diperkirakan dari melakukan lebih banyak per batch. Untuk batch 100 saya mendapatkan sekitar 251ms, dengan 10.000 saya mendapatkan 208ms.
Saya harus mengutip nama bidang karena cara RPostgreSQL membuat tabel
dbWriteTable
Itu sekitar 4x secepat jika saya melakukannya satu rekaman pada satu waktu. Ketika saya melakukannya satu per satu, saya bisa mendapatkan rincian berdasarkan negara (lihat di bawah). Saya melakukan ini untuk memeriksa dan melihat apakah satu atau lebih negara TIGER memiliki muatan atau indeks yang buruk, yang saya perkirakan menghasilkan
geocode
kinerja yang buruk secara statistik. Saya jelas punya beberapa data buruk (beberapa alamat bahkan alamat email!), Tetapi sebagian besar diformat dengan baik. Seperti yang saya katakan sebelumnya, beberapa kueri berjalan terpanjang tidak memiliki kekurangan yang jelas dalam formatnya. Di bawah ini adalah tabel angka, waktu kueri minimum, waktu kueri rata-rata, dan waktu kueri maks untuk negara bagian dari 3000-beberapa alamat acak dari dataset saya:sumber