Veritabanında çok geniş bir dizge / kayıt listesi içinde hızlıca arama


32

Aşağıdaki sorunu yaşıyorum: 2 milyondan fazla kayıt içeren bir veritabanım var. Her kaydın bir X string alanı vardır ve X alanının belirli bir dize içerdiği kayıtların bir listesini görüntülemek istiyorum. Her kaydın boyutu yaklaşık 500 bayttır.

Daha somutlaştırmak için: başvurumun GUI'sinde bir dize girebileceğim bir metin alanına sahibim. Metin alanının üstünde, metin alanındaki dizeyle eşleşen (ilk N, örneğin 100) kayıtları gösteren bir tablom var. Metin alanına bir karakter yazdığımda veya sildiğimde, tablo içeriği anında güncellenmelidir.

Bunu uygun indeks yapıları ve / veya önbellek kullanarak yapmanın etkili bir yolu olup olmadığını merak ediyorum. Yukarıda açıklandığı gibi, yalnızca sorguyla eşleşen ilk N öğesini görüntülemek istiyorum. Bu nedenle, N küçük için, eşleşen öğeleri veritabanından yüklemek büyük bir sorun olmamalıdır. Ayrıca, ana bellekteki öğelerin önbelleğe alınması, erişimi daha hızlı hale getirebilir.

Bence asıl problem, kalıp dizgisine verilen eşleme maddelerinin hızlıca nasıl bulunacağı. Bazı DBMS özelliklerine güvenebilir miyim, yoksa bazı bellek içi endeksleri kendim mi oluşturmalıyım? Herhangi bir fikir?

DÜZENLE

İlk deneyi yaptım. Kayıtları farklı metin dosyalarına böldüm (dosya başına en fazla 200 kayıt) ve dosyaları farklı dizinlere koydum (dizin ağacını belirlemek için bir veri alanının içeriğini kullandım). 40000 dizinde yaklaşık 50000 dosya ile son bulurum. Dosyaları indekslemek için Lucene'yi çalıştırdım. Lucene demo programı ile bir dize aramak oldukça hızlı. Bölme ve dizin oluşturma birkaç dakika sürdü: bu benim için tamamen kabul edilebilir çünkü sorgulamak istediğim statik bir veri seti.

Bir sonraki adım, Lucene'yi ana programa entegre etmek ve ilgili kayıtları ana hafızaya yüklemek için Lucene'nin geri gönderdiği isabetleri kullanmaktır.


2
2 milyon kayıt * 500 bayt = 1 GB veri. Bu, araştırmak için çok fazla veri, hangi yoldan giderseniz gidin - her bir X değerinin benzersiz olması muhtemel mi, yoksa aynı X değerine sahip birçok kaydın mı olacak?

1
Bu, hızlı alım için önbellek olarak bellekte depolamaya çalışmak için çok fazla veri olacaktır. Her kullanıcı oturumu için 1GB'tan fazla olacaktır.
maple_shaft

Önceki yorumum bir web uygulaması olduğunu varsayar. Bu bir web uygulaması mı?
maple_shaft

Bu bir masaüstü uygulamasıdır. Kayıtlardaki değerler mutlaka benzersiz değildir. Ayrıca, tam eşleşmeyi değil alt dizgiyi arıyorum.
Giorgio

@maple_shaft: Sadece son zamanlarda girdiğim kayıtları önbelleğe alırdım. Sorgu dizesini değiştirirsem ve bir kayıt hala eşleşirse, yine de önbellekte olur.
Giorgio

Yanıtlar:


20

Verilerinizi DB içine koymak yerine, bunları ayrı ayrı bir belge kümesi (metin dosyaları) olarak tutabilir ve bağlantıyı (yol / URL vb.) DB'de saklayabilirsiniz.

Bu önemlidir, çünkü tasarım gereği SQL sorgusu hem alt dizede aramada hem de alımda çok yavaş olacaktır.

Şimdi, probleminiz, dizeleri içeren metin dosyalarını aramak zorunda kalmak şeklinde formüle edildi. Burada iki olasılık var.

  1. Alt-dize eşleşmesi Eğer metin bloblarınız tek bir acı ya da kelime ise (herhangi bir beyaz boşluk olmadan) ve içinde rastgele bir alt-dizinde arama yapmanız gerekir. Bu gibi durumlarda, eşleşen en iyi dosyaları bulmak için her dosyayı ayrıştırmanız gerekir. Bir Boyer Moor algoritması gibi algoritmalar kullanır. Ayrıntılar için buna ve buna bakın. Bu ayrıca grep'e eşdeğerdir - grep, içinde benzer şeyler kullanır. Ancak, geri dönmeden önce hala en az 100+ grep (en kötü durum 2 milyon) yapabilirsiniz.

  2. Dizinlenmiş arama. Burada, metnin bir dizi kelime içerdiğini ve aramanın sabit kelime uzunlukları ile sınırlı olduğunu varsayıyorsunuz. Bu durumda, belge kelimelerin olası tüm oluşumları üzerinde indekslenir. Buna genellikle "Tam Metin araması" denir. Bunu yapmak için algoritmalar ve doğrudan kullanılabilecek açık kaynak projeleri sayısı var. Birçoğu, aşağıdaki gibi joker arama, yaklaşık arama vb. Destekler:
    a. Apache Lucene: http://lucene.apache.org/java/docs/index.html
    b. OpenFTS: http://openfts.sourceforge.net/
    c. Sfenks http://sphinxsearch.com/

Sorgu olarak "sabit kelimelere" ihtiyacınız varsa, büyük olasılıkla iki yaklaşımı çok hızlı ve etkili olacaktır.


2
Bu ilginç bir kavram ama bir geliştiricinin 1GB metin verilerini bir veritabanı motorundan daha hızlı ve daha verimli şekilde arayabilmesi pek mümkün görünmüyor. Senden çok daha zeki insanlar ve bunu yapmak için sorgu iyileştiricileri üzerinde çok çalıştık ve bir şekilde bunu daha verimli yapabileceğini düşünmek biraz saf.
maple_shaft

4
@maple_shaft Verdiğim örnekler RDBMS veritabanı motorları değil. Aramak istersen daha çok "arama motorları" gibiler. Bir dizin (veya karma tablo) listesinden bir liste almak ile bir sorgu ateşlendiğinde her seferinde 1GB veride tekrar arama yapmak arasında büyük bir kavramsal fark vardır. Yani önerdiğim şey küçük bir değişiklik değil.
Dipan Mehta

Bu ilginç bir fikir gibi görünüyor ama işe yarayacağını merak ediyorum. Her biri yaklaşık yarım kilobayt büyüklüğünde 2 000 000 dosyadan fazlasına sahip olurdum. Yoksa dosya başına birden fazla kayıt olmasını mı öneriyorsun? Veritabanına göre fark ne olurdu?
Giorgio

Bunun mutlaka SQL tam metin dizininden daha iyi performans gösterdiğine ikna olmadım.
Kirk Broadhurst

@Giorgio - evet, tam metin arama motorlarının çalışması budur. Buradaki en önemli fark, önceden kaydedilmiş sayfalara karşılık bellek içi aramadır (bir sorgu her geldiğinde tekrar).
Dipan Mehta

21

Aradığınız teknoloji tam metin indekslemedir. Çoğu RDBMS burada işe yarayacak bir tür yerleşik yeteneklere sahiptir ya da meraklısı olmak ve / veya sadece bellekte çalıştırmak istiyorsanız Lucene gibi bir şey kullanabilirsiniz.


1
Kanımca, herhangi bir RDBMS'deki tam metin seçenekleri, tasarlanmadığı bir şeyi yapması için geçici bir çözüm olduğunu: "yapılandırılmamış, ilgisiz veri yığınında arayın". Bir arama motoru oluşturuyorsanız, yalnızca bir RDBMS kullanmazsınız. Küçük veri kümeleri için işe yarayabilir ancak her türlü ölçeklendirmeyi sağlar. Yapılandırılmamış veri yığınlarını aramak bir çivi değildir, bu yüzden çekiç kullanmayın. İş için doğru aracı kullanın.
Pieter B,

8

Bir trie düşündün mü ? Temel olarak ortak ön ekler kullanarak bir ağaç oluşturursunuz, bu nedenle aynı harflerle başlayan tüm kelimeler aynı düğümün çocuklarıdır. Herhangi bir alt dize eşleştirmeyi destekleyecekseniz, o zaman bir tür izinli dizin oluşturup bu sayede bilginizi oluşturmak zorunda kalırsınız . Yine de, depolama gereksinimlerinizi dışarı atıyor olabilir.


1
EVET! Bir ağaç yapısını düşünüyordum ve bana uygun olabilecek bir şey olduğunu hatırladım, ama trie'leri hatırlayamadım çünkü onları hiç kullanmadım. Depolama gereksinimi ile ilgili olarak: yalnızca ilk N girişini (örneğin N = 100) almam gerektiğini, çünkü 20000 hit içeren bir tabloyu doldurmanın bir anlamı olmadığını unutmayın. Dolayısıyla, trie düğümünün her düğümü en fazla N girişine işaret edecektir. Ayrıca, hızlı erişime ihtiyacım olduğunu belirtmeyi unuttum, ancak hızlı bir güncellemeye ihtiyacım yok, çünkü veriler yalnızca bir kez yüklendi. İzin verilen bir endeks üzerindeki trie fikri gerçekten işe yarayabilir!
Giorgio

1
İyi cevap, ama not ettiğiniz gibi, bir kelime kelimelerinin başlangıcını eşleştirmek için harikadır, ancak herhangi bir alt
diziyi

İlk deney olarak, dizgelerde görünen tüm alt dizgilerin kümesini oluşturmaya çalıştım, aramam gereken, eğer doğru anlarsam, trie'nin yollarına karşılık gelir. 6. uzunluktaki alt dizgilerde (JVM için 256M yığın ile) bir bellek dışı istisna aldım. Bu yüzden yanlış bir şey yapmadığım sürece bu çözümün mümkün olmadığını düşünüyorum.
Giorgio

5

Wyatt Barnett'in cevabını, uygun sütunda tam metin indeksleme özelliğine sahip bir RDBMS çözümünün işe yarayacağına cevabını eklemek isterim, ancak daha önce alınmış kayıtların yerel önbelleğini kullanmak istiyorsanız, bu önbelleğe alınmış kayıtları kullanmak için bir plan yapmanız gerekir. Avantajınız için.

Seçeneklerden biri, bu kayıtların TAMAMEN sorgusuzca almak istemediğiniz benzersiz tanımlayıcılarını toplamak ve bunları muhtemelen bir NOT INveya a NOT EXISTS.

Ancak dikkatli olun, kullanmak NOT INveya NOT EXISTSucuz olmamak eğilimindedir ve kullandığınız veritabanı motoruna bağlı olarak sorgu performansınızı veya sorgu planınızı olumsuz yönde etkileyebilir. Etkilenen sütunlardaki tüm dizinlerin kullanıldığından emin olmak için son sorgunuzda bir açıklama planı kullanın.

Ayrıca hangisinin daha hızlı olduğunu görmek için iki yaklaşım arasında bir performans karşılaştırması yapmaktan zarar gelmez. Yerel bir önbelleği korumanın ve bunları sorgudan açıkça filtrelemenin, tüm kayıtları alan ince ayarlanmış bir sorgudan daha kötü bir performans gösterdiğini görünce şaşırabilirsiniz.


maple_shaft ve @Wyatt Barnett: Önerileriniz için çok teşekkürler. Biraz okuma yapmak ve farklı çözümler denemek zorunda kalacağım. Tüm veritabanları tam indekslemeyi desteklemez, şu anda kullanıyorum MySQL ( dev.mysql.com/doc/refman/5.5/en/fulltext-search.html ) işlevini yerine getirir . Bazı testler yapmaya çalışacağım ve daha sonra rapor edeceğim.
Giorgio

2

Sadece kaçırmış olmanız durumunda. Lucene'yi veritabanı içinde desteklenen metin arama yerine veritabanınız için kullanıyorsanız, veritabanınızda değişiklik yaparken çok dikkatli olmanız gerekir. Hem DB'de hem de harici kaynaklarda (Lucene) değişiklik yapmanız gerektiğinde, atomikliğe sahip olduğunuzdan nasıl emin olabilirsiniz? Evet yapılabilir, ancak çok fazla iş olacak.

Kısacası, Lucene'i veri şemanıza yerleştirirseniz, DB işlem desteğini kaybedersiniz.


1
Belirtildiği gibi herhangi bir şekilde bir RDMS için uygun bir sorun gibi görünmüyor.
Pieter B

1

Sfenks'i düşündün mü? http://sphinxsearch.com , bir üçüncü taraf aracını kullanabiliyorsanız, bu elde etmeye çalıştığınız şey için ideal olacaktır, tam metin aramada kişisel olarak kullandığım herhangi bir RDBMS'den çok daha verimlidir.


3
ve aşağı oy için ne?
twigg

1

Apache Lucene ve diğerlerine benzer tüm çözümlerin altında yatan teknoloji olan cevapların hiçbirinin “ters endeks” terimini sunmaması biraz garip .

Tersine çevrilmiş dizin, kelimelerden belgelere ("kayıt düzeyinde ters çevrilmiş dizin") veya hatta belge içindeki kesin kelime konumlarına ("kelime düzeyinde ters çevrilmiş dizin") yapılan bir eşlemedir.

VE ve VEYA mantıksal işlemler gerçekleştirmek için çok önemlidir. Kesin kelime konumlarınız varsa, bitişik kelimeleri aramak mümkündür, böylece cümle aramalarını mümkün kılar.

Bu yüzden, (kelime, dosya, konum) dişleri içeren bir dizin düşünün. Örneğin ("ters", "foo.txt", 123) varsa, o zaman ("index", "foo.txt", 124) "tümceciği" için tüm dizini aramak üzere dizinin bir parçası olup olmadığını kontrol edin. .

Tam metinli bir arama motorunu sıfırdan yeniden biçimlendirmenizi tavsiye etmemekle birlikte, Apache Lucene gibi teknolojilerin nasıl çalıştığını bilmek faydalıdır.

Bu yüzden benim önerim ters indekslerin nasıl çalıştığını öğrenmek ve bunları Apache Lucene gibi bir teknolojiyi seçmek. Öyleyse, en azından ne yapılabileceğini ve ne yapılamayacağını anlamanız yeterli.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.