it-swarm.dev

Veritabanı: Coğrafi konum verilerini sorgulamanın en iyi yolu?

Bir MySQL veritabanım var. Veritabanında evleri saklıyorum ve veri tabanına karşı tam anlamıyla sadece 1 sorgu gerçekleştiriyorum,fakat süper hızlıbu sorguya ihtiyacım var, ve bu, tüm evlerin kare kutu coğrafi enlem ve boylamları içinde döndürülmesi.

SELECT * FROM homes 
WHERE geolat BETWEEN ??? AND ???
AND geolng BETWEEN ??? AND ???

Coğrafi verilerimi depolamamın en iyi yolu, coğrafi konum kutusundaki tüm evleri en hızlı şekilde görüntülemek için bu sorguyu gerçekleştirebilmemdir?

Temelde:

  • Bu sorguyu en hızlı şekilde gerçekleştirmek için en iyi SQL deyimini kullanıyorum?
  • Kutulu bir coğrafi konum sınırları içindeki evlerin sonucunu en hızlı şekilde sorgulamam için bir veritabanı bile kullanmamış başka bir yöntem var mı?

Yardımcı olması durumunda, veritabanı tablo şememi aşağıya ekledim:

CREATE TABLE IF NOT EXISTS `homes` (
  `home_id` int(10) unsigned NOT NULL auto_increment,
  `address` varchar(128) collate utf8_unicode_ci NOT NULL,
  `city` varchar(64) collate utf8_unicode_ci NOT NULL,
  `state` varchar(2) collate utf8_unicode_ci NOT NULL,
  `Zip` mediumint(8) unsigned NOT NULL,
  `price` mediumint(8) unsigned NOT NULL,
  `sqft` smallint(5) unsigned NOT NULL,
  `year_built` smallint(5) unsigned NOT NULL,
  `geolat` decimal(10,6) default NULL,
  `geolng` decimal(10,6) default NULL,
  PRIMARY KEY  (`home_id`),
  KEY `geolat` (`geolat`),
  KEY `geolng` (`geolng`),
) ENGINE=InnoDB  ;

GÜNCELLEME

Mekansal iradenin yeryüzünün eğriliğini etkilediğini anlıyorum, ancak en çok coğrafi verilere en hızlı şekilde geri dönmekle ilgileniyorum. Bu uzamsal veritabanı paketleri bir şekilde daha hızlı veri döndürmezse, lütfen uzamsal uzantıları önermeyin. Teşekkürler

GÜNCELLEME 2

Lütfen dikkat, aşağıdaki hiç kimse soruyu tam olarak yanıtlamadı. Gerçekten alabileceğim herhangi bir yardımı bekliyorum. Şimdiden teşekkürler.

36
HankW

MySQL coğrafi konumlandırma performansı hakkında iyi bir makale var here .

EDIT Bunun sabit yarıçap kullandığından emin olun. Ayrıca, mesafeyi hesaplamak için kullanılan algoritmanın en gelişmiş olduğu% 100 emin değilim.

Önemli olan, algoritmanın uygun mesafe araması yapmak için size sıra sayısı üzerinde bir top parkı sınırı vermenin ucuz olmasıdır.


Algoritma, kaynak noktanın etrafındaki bir kareye aday alarak ve ardından mesafeyi mil olarak hesaplayarak ön filtreleme yapar.

Bunu önceden hesaplayın veya kaynağın önerdiği gibi saklı bir prosedür kullanın:

# Pseudo code
# user_lon and user_lat are the source longitude and latitude
# radius is the radius where you want to search
lon_distance = radius / abs(cos(radians(user_lat))*69);
min_lon = user_lon - lon_distance;
max_lon = user_lon + lon_distance;
min_lat = user_lat - (radius / 69);
max_lat = user_lat + (radius / 69);
SELECT dest.*,
  3956 * 2 * ASIN(
    SQRT(
      POWER(
        SIN(
          (user_lat - dest.lat) * pi() / 180 / 2
        ), 2
      ) + COS(
        user_lat * pi() / 180
      ) * COS(
        dest.lat * pi() / 180
      ) * POWER(
        SIN(
          (user_lon - dest.lon) * pi() / 180 / 2
        ), 2
      )
    )
  ) as distance
FROM dest
WHERE 
  dest.lon between min_lon and max_lon AND
  dest.lat between min_lat and max_lat
HAVING distance < radius
ORDER BY distance
LIMIT 10
13
Igor Zevaka

Ben de aynı problemi yaşadım ve 3 bölümlük bir blog yazısı yazdım. Bu coğrafi endeksten daha hızlıydı.

Giriş , Benchmark , SQL

4
Evert

Performans için gerçekten gitmeniz gerekiyorsa, verileriniz için sınırlayıcı kutular tanımlayabilir ve ön hesaplama sınırlayıcı kutularını yerleştirme sırasında nesnelerinize eşleyebilir ve bunları daha sonra sorgular için kullanabilirsiniz.

Sonuç kümeleri oldukça küçükse, doğru sonuçlar sunarken, uygulama mantığında (bir veritabanına göre yatay ölçeklendirmek daha kolay) yine de doğruluk düzeltmeleri yapabilirsiniz.

Yaklaşım için harika belgeler içeren Bret Slatkin'in geobox.py adresine bir göz atın.

Öngörülebilir gelecekte daha karmaşık sorgular yapmak istiyorsanız, PostgreSQL ve PostGIS 'in MySQL ile karşılaştırılmasını kontrol etmenizi tavsiye ederim.

2
tosh

İşte bazı başarılarla kullandığım bir püf noktası, yuvarlama bölgeleri oluşturmak. Diğer bir deyişle, 36.12345, -120.54321 olan bir konumunuz varsa ve onu yarım mil (yaklaşık) ızgara kutusundaki diğer konumlarla gruplandırmak istiyorsanız, bölge 36.12x-120.54’ü arayabilirsiniz. aynı yuvarlama bölgesine sahip tüm diğer yerler aynı kutuya düşecektir.

Açıkçası, bu size temiz bir yarıçap getirmez, yani baktığınız yer bir Kenardan diğerine daha yakınsa. Ancak, bu tür bir kurulumla, ana konumunuzun kutusunu çevreleyen sekiz kutuyu hesaplamak yeterince kolaydır. Zekâ:

[36.13x-120.55][36.13x-120.54][36.13x-120.53]
[36.12x-120.55][36.12x-120.54][36.12x-120.53]
[36.11x-120.55][36.11x-120.54][36.11x-120.53]

Eşleşen yuvarlak etiketlere sahip tüm yerleri çekin ve ardından bunları veritabanından çıkardıktan sonra, hangilerini kullanacağınızı belirlemek için mesafe hesaplamalarınızı yapabilirsiniz.

1
Ben

Kullanmakta olduğunuz endeksler gerçekten B-ağacı endeksleridir ve sorgunuzdaki BETWEEN anahtar sözcüğünü destekler. Bu, optimize edicinin endekslerinizi "kutunuzdaki" evleri bulmak için kullanabileceği anlamına gelir. Bununla birlikte, her zaman endeksleri kullanacağı anlamına gelmez. Çok fazla "isabet" içeren bir aralık belirtirseniz, indeksler kullanılmaz.

1
Peter Lindqvist

Çünkü MySQL 5.7 mysql, performansı artıran ST_Distance_Sphere () ve ST_Contains () gibi geoindex kullanabilmektedir.

0
Anak1

Çok iyi bir alternatif MongoDB ile Geospatial Indexing .

0
jalogar

Evler? Muhtemelen on bine bile sahip olmayacaksın. Yalnızca STRTree gibi bir bellek içi dizin kullanın.

0
novalis

Bu oldukça hızlı görünüyor. Tek kaygım, tüm değerleri enlemin 3 mil içindeki tüm değerleri almak için bir dizin kullanmak, ardından da boylamın 3 mil içindeki değerler için filtrelemek olacaktır. Altta yatan sistemin nasıl çalıştığını anlarsam, tablo başına yalnızca bir INDEX kullanabilirsiniz, bu nedenle lat veya long dizinindeki değer yoktur.

Çok miktarda veriye sahipseniz, might her 1x1 mil kareye benzersiz bir mantıksal kimlik vermek için işleri hızlandırır ve ardından SELECT seçeneğine ek bir kısıtlama getirir (area = "23234/34234" OR area = "23235/34234" OR ...) noktanızın etrafındaki tüm kareler için, veritabanını lat ve uzun yerine bu dizini kullanmaya zorlayın. O zaman sadece çok daha az mil kare veriyi filtreleyeceksiniz. 

Mevcut yaklaşımınıza bağlı kalarak yapmanız gereken tek bir değişiklik var,

KEY `geolat_geolng` (`geolat`, `geolng`),

Şu anda sorgunuz yalnızca iki dizinden birinden faydalanıyor olacak.

0
Ben

Bir birincil anahtarı ('geolat', 'geolng') olan ve belirli bir coğrafi konumun bir evi varsa, home_id değerini içeren bir sütun içeren ayrı bir 'GeoLocations' tablosu oluşturabilirsiniz. Bu, optimize edicinin, bir home_ids listesi için diskte sıralanacak bir dizi coğrafi konum aramasını sağlar. Ardından, bu home_ids hakkında bilgi edinmek için 'evler' masanıza katılabilirsiniz.

CREATE TABLE IF NOT EXISTS `GeoLocations` (
`geolat` decimal(10,6) NOT NULL,
`geolng` decimal(10,6) NOT NULL,
`home_id` int(10) NULL
PRIMARY KEY  (`geolat`,`geolng`)
);

SELECT GL.home_id
FROM GeoLocations GL
INNER JOIN Homes H
 ON GL.home_id = H.home_id
WHERE GL.geolat between X and Y
 and GL.geolng between X and Y
0
Clayton Stewart