Sıralanmış bir listeyi depolamak için bir veritabanı nasıl tasarlanır?


42

Bir veritabanı içinde sıralanmış bir liste saklamak için arıyorum. Aşağıdaki işlemleri verimli bir şekilde yapmak istiyorum.

  1. Ekle (x) - Tabloya x kaydı ekle
  2. Sil (x) - Tablodan x kaydını sil
  3. Before (x, n) - Sıralanan listedeki x kaydından önceki 'n' kayıtlarını döndür.
  4. Sonra (x, n) - Sıralanan listedeki x kaydının ardından 'n' kayıtlarını döndür.
  5. İlk (n) - İlk 'n' kayıtlarını sıralanmış listeden döndür.
  6. Son (n) - Sıralanan listeden son 'n' kayıtlarını döndür.
  7. Compare (x, y) - Tablodan iki ve iki kayıt verilmişse, x> y olup olmadığını bulun.

Aklıma gelen basit bir yöntem, tablodaki bir çeşit 'sıralama' niteliğini depolamak ve bu niteliği sıralayarak sorgulamaktır. Ancak bu yöntemde bir rütbe ile bir kayıt ekleme / değiştirme masraflı bir işlem haline gelir. Daha iyi bir yöntem var mı?

Özellikle, Amazon'un SimpleDB'sini kullanarak tabloyu uygulamak istiyorum. Ancak ilişkisel veritabanı için genel bir cevap da yardımcı olmalıdır.

Yük profilinde güncelleme:

Bunu bir web uygulaması için planladığımdan, uygulamayı kullanan kullanıcıların sayısına bağlı.

100 k aktif kullanıcı varsa (süper iyimserlik: P), o zaman günlük yaklaşık tahminim

500k seçer, 100k ekler ve siler, 500k güncelleme

Masanın toplamda 500 bin dolara kadar büyümesini beklerdim.

Güncelleştirmeler, ekleme ve Karşılaştırma işlemlerinde optimizasyon yapmak istiyorum. Maddelerin sırası sürekli değişecek ve masayı güncel tutmam gerekiyor.


Beklenen yük profilinde biraz detaylandır. Günde kaç tane seçme / ekleme / güncelleme yapılıyor? En çok hangi işlemler için optimize edilmesini istiyorsunuz? Masanın günde ne kadar büyüyeceğini veya toplamda ne kadar büyük olacağını düşünüyorsunuz?
Nick Chammas 13:11

Bu bir oyuncu sıralaması kurulu için mi? Her neyse, cevabımı aşağıdaki öngörülen yük profilinize göre geri bildirimlerle güncelledim.
Nick Chammas

hayır, oyuncu sıralaması kurulu değildir.
chitti

Hangi yaklaşımı kullanmaya başladın?
Nick Chammas

Burada ne sorulduğundan veya ne yapmanız gerektiğinden çamaşırhane listesinden ne yapmanız gerekmediğinden bile emin değilim.
Evan Carroll

Yanıtlar:


22

Rütbe tamamen keyfi değilse de bunun yerine başka bir mülkünden türetilebilirse (örn. İsim, oyuncu puanı vb.), O zaman Joel'in cevabına dikkatlice bakın .

O takdirde ise verilerinizin keyfi bir özellik, o kayıtların Tablonuzdaki bir sütun olarak muhafaza edilmelidir. Amazon'un SimpleDB'sinin tipik RDBMS'ye benzer olduğunu varsayarak, bu sütunu dizine ekleyebilir ve yukarıdaki sorgularınızı hızlı bir şekilde uygun dizin oluşturma stratejisiyle karşılayabilirsiniz. Bu bir RDBMS için normaldir.

Yüksek ekleme ve güncelleme etkinliği, aynı zamanda nispeten yüksek okuma etkinliği beklediğinizden, aşağıdakileri yapmanızı öneririm:

  • Masayı, özellikle de sorularınızın büyük çoğunluğu rütbeye aykırıysa, kümeye yerleştirin. Değilse veya bir kümeleme anahtarı seçmek SimpleDB'de kullanılamıyorsa, yalnızca ana sütun olarak rütbeli bir dizin oluşturun. Bu sorgular 3-6 yerine getirir.
  • İlk önce kayıt ve ardından bir sıra (veya SQL Server dünyasında, sadece kayıt ve INCLUDEsıralaması veya sadece sıralamaya göre kümelendiyseniz kayıt) 7. sorguyu tatmin edecektir.
  • İşlem 1 ve 2 verilerinizi uygun şekilde ayırarak optimize edilebilir (örneğin FILLFACTOR, SQL Server'da ayarlama ). Bu, özellikle rütbe ayarlanmışsanız önemlidir.
  • Sıraları eklerken veya güncellerken, bir sıra eklemesi veya güncellemesi için mevcut bir kaydı tekrar sıralamanız gerekme olasılığını en aza indirmek için sıra sayıları arasında mümkün olduğunca fazla boşluk bırakın. Örneğin, kayıtlarınızı 1000'lik adımlarla sıralarsanız, yarısı kadar az değişiklikle ve en az şansa sahip olan ekler için, bu değişikliklerle doğrudan ilgisi olmayan bir kaydı yeniden sıralamanız gerekecek yarıya kadar yer bırakırsınız.
  • Her gece aralarındaki sıralama boşluklarını sıfırlamak için tüm kayıtları yeniden sırala.
  • Mevcut kayıt sayısına göre beklenen ekleme veya güncelleme sayısını karşılamak için kütle sıralamalarının sıklığını ve sıralama aralığı büyüklüğünü ayarlayabilirsiniz. Eğer 100K kayıtlarınız varsa ve eklerinizin ve güncellemelerinizin% 10 olmasını beklerseniz, 10K yeni rütbe için yeterli yer bırakın ve her gece yeniden rütbe alın.
  • 500K kayıtların yeniden sıralanması pahalı bir işlemdir, ancak günde bir kez veya haftada bir kez çalışıldığında, çalışma saatleri böyle bir veritabanı için iyi olmalıdır. Bu aralık dışı kütle, sıralamadaki boşlukları korumak için yeniden sıralama yapmaktır;

100K + 'lık bir 100K + büyüklüğünde tablo okumayı düşünüyorsanız, bağlantılı liste yaklaşımını kullanmanızı önermiyorum. Bu boyutlara iyi ölçeklendirilmez.


Sıralar değiştirilebilir. Rütbelerin sürekli değişmesini ve yeni kayıtların sürekli eklenmesini bekliyorum. Sıralamada yeni bir eleman eklediğimde durum hakkında endişeliyim, sonra sıralama sırasındaki tüm kayıtların sıralama sıralamasının değişmesi gerekiyor. Veritabanımda binlerce kayıt bulunduğunda bu pahalı bir işlem değil mi?
chitti

@ chitti - Ah, bu bir endişe. Sıralarınızı boşaltabilirsiniz (örn. 0, 1000, 2000, 3000, ...) ve sıradaki boşluklar doldurulurken tüm kayıtları periyodik olarak yeniden sıralayabilirsiniz. Yine de, birkaç on binlerce kayıttan fazlasını beklerseniz, bu ölçeklenmeyecektir.
Nick Chammas

1
@chitti - Bu aslında biraz komik. Bu tam olarak veritabanı motorlarının verileri indekslerken ele aldıkları problemdir, çünkü onu sıralıyorlar ve veri eklendikçe veya değiştirildikçe yeniden sıralıyorlar. Eğer bakarsanız FILLFACTOR, temelde bir dizindeki kayıtlar için bu fazladan boşluk yaratmanın, tıpkı tanımladığım sıralama boşluklarının sıralama değişiklikleri ve eklemeleri için alan yaratması gibi olduğunu göreceksiniz.
Nick Chammas

2
Güncellenmiş cevap için teşekkürler. 'Rütbe' verilerimin keyfi bir özelliğidir. Özel bir indeks sütununun ihtiyacım olan şey olduğuna neredeyse ikna oldum. Benzer bir soru ile bu SO bağlantısını kontrol edin . En üstteki cevap, böyle bir sıralama sütununun nasıl kullanılacağına dair öneriler sunar.
chitti

@chitti - Bu SO sorununun kabul ettiği cevap harika. Burada ayrıntılandırdığım aynı yaklaşımı, sıraları atama ve değiştirme esnekliğinizi büyük ölçüde genişletmek için tamsayılar yerine ondalık sayıları kullanma önerisini önerir. Harika bul
Nick Chammas

13

Genelde tarif ettiğiniz "rütbe" metodunu kullanırım. Öğelerin yeniden sıralanması gerektiğinde güncelleme satırlarıyla uğraşmak yerine sık sık listedeki tüm kayıtları silmekten ve yeni öğeleri uygun sırayla yeniden yerleştirmekten kurtuldum. Bu yöntem, alım için açıkça optimize edilmiştir.

Alternatif bir yaklaşım, kayıtları tablodaki "öncül" bir dönüşlü yabancı anahtar sütunu kullanarak bağlantılı bir liste olarak modellemektir:

ID   setID   item       predecessor
---  ------  ------     ------------
1    1       Apple      null
2    1       Orange     1
3    2       Cucumber   null
4    1       Pear       2
5    1       Grape      4
6    2       Carrot     3

Kolayca bir liste alabilir ve ek yükü az olan öğeleri ekleyebilir ve çıkarabilirsiniz, ancak kayıtları uygun sırayla almak zor olacaktır. Belki de bunu tek bir sorguda yapmanın akıllıca bir yolu vardır, muhtemelen birçok takma tablo birleşimi ile.

Ağaç stili bir ilişkiyi modellerken, bu kategoriyi sıklıkla kullanırım (kategoriler, klasörler, kümeler ve alt kümeler). Genelde, uygulamamdaki tüm ağacı yeniden yapılandırmak için bir çeşit özyinelemeli işlev görmüştüm.


2
Bağlantılı liste modeli temiz. Bu tür bir hiyerarşiyi SQL Server'da sırayla almak için özyinelemeli bir CTE kullanırsınız .
Nick Chammas 13:11

Bu hiyerarşiyi inşa etmek uzun bir masa için oldukça maliyetli olurdu. Bunun avantajı, rütbe değişikliklerinin / eklerin / etc'nin kolayca yapılabilmesidir. Chitti'nin beklenen yük profiline bağlı olarak, bu aslında en iyi yaklaşım olabilir.
Nick Chammas 13:11

Bağlantılı liste seçeneği, Karşılaştırma dışındaki tüm işlemler için en iyi fikir gibi görünüyor. Karşılaştığım herhangi bir fikrin Karşılaştırılan iki element arasındaki yolu izlemeden Karşılaştırma?
chitti

Karşılaştırma ile ne demek istediğinizi yanlış anlamadığım sürece Compare () 'nin basit olduğunu düşündüğüm öğelerin kimliklerini varsa. Derken: "x> y olup olmadığını bul" mu demek istediniz "x" y'den önce gelirse bul "mu? Özel bir dizin veya listede yürüyecek bir saklı yordam olmadan (veya @Nick tarafından belirtilen ilginç CTE özelliği) bunun kolay olmadığını göremiyorum.
bpanulla,

5
Bu çözüm türü aynı zamanda bir grafik veri modeline de yaklaşmaktadır ( en.wikipedia.org/wiki/Graph_theory ). Grafik düğümlerini ve kenarlarını depolamak için optimize edilmiş bir depolama sistemi, bir RDBMS'den daha iyi bir çözüm olabilir. Üçlü ve Dörtlü mağazalar ve Neo4J gibi grafik veritabanları bu konuda oldukça iyi.
bpanulla,

6

Yapılması gereken, rütbeyi hesaplamak için kullanılan mülk veya mülkleri depolamak ve daha sonra bunların üzerine bir dizin oluşturmak olduğunu düşünüyorum. Veritabanını, verileri fiziksel olarak sıralı sırayla depolamaya veya elle yönetilen bir bağlı liste kullanarak zorlamak yerine, neden veritabanı motorunun yapmak üzere tasarlandığı şeyi yapmasına izin vermiyorsunuz?


2
'Sıralamayı hesaplamak için kullanılan özellikler' isteğe bağlıysa ne olur? Örn: Kullanıcının keyfi işlemlerine göre yeniden sıralanan bir alışveriş sepeti girişi.
chitti

Rütbenin keyfi olduğunu söylerken, ne demek istiyorsun? Rütbenin ne olacağını hesaplamak için kullandığınız bir algoritma olmalıdır. Örneğin: "alışveriş sepetindeki girişlere göre" - Nasıl yapılır? Rütbe hesaplamasının sürücüsü olan veritabanında depolanan bir şey olmalıdır. Birkaç şeyin bir kombinasyonu olabilir, ancak bu şeyler bir şekilde müşteri masasında veya müşteri ile ilgili tablolarda saklanmalıdır. Verilerdeyse, onu hesaplayan bir işlev oluşturabilirsiniz. Eğer hesaplayabilirseniz, saklayabilir ve üzerine endeksleyebilirsiniz.
Joel Brown,

Bir alışveriş sepetindeki öğelerin sırasını korumamız gerektiğini ve siparişin bir web kullanıcı arayüzü kullanarak kullanıcı tarafından 'keyfi olarak' değiştirilebileceğini varsayalım. Bu tür bir listeyi bir veritabanında nasıl saklarsınız ve sıralama düzenini nasıl korursunuz?
chitti

Sizi doğru anlarsam, bir alışveriş sepetindeki öğelerin sırasını "rasgele değiştirerek" kullanıcının öğeleri listede yukarı ve aşağı sürükleyip istediği yere bırakabileceği anlamına gelir. Sanırım bu beni biraz hakaret etti. Kullanıcılar bunu neden yapıyor? Eğer yapabilselerdi, çok mu yaparlardı? Bir el arabasında basit bir ürün dizisi kullanmak gerçekten bu kadar fazla performans endişesi mi yaratıyor? Bana göre, sepetten bir sıra numarasına + FK sırasına kadar olan sıra numarası size ihtiyacınız olan dizini verecektir. Biri sürüklendiğinde öğeleri güncelle.
Joel Brown,

3
Alışveriş sepeti, 'rütbenin' keyfi olabileceği durumlar olduğunu göstermek için verdiğim bir örnek. Bu iyi bir örnek değildi olabilir. Netflix dvd sırası daha iyi bir örnek olabilir. Sadece tartışma uğruna, kullanıcı tarafından keyfi bir şekilde yeniden sıralanabilecek 100k öğeden oluşan bir netflix kuyruğunu hayal edin ve her dakikasında bir yaptı. Bu sıralı uygulamada, sıralanan film listesini saklamak için bir veritabanı nasıl tasarlarsınız?
chitti

1

Bunlar simpleDB gibi RDBMS olmayanların sınırlamalarıdır. İhtiyacınız olan özellikler, DB tarafında simpleDB'de uygulanamaz, programlama tarafında / uygulamada uygulanmaları gerekir.

Bunun gibi bir RDBMS için SQL server, istediğiniz özellikler kümelenmiş dizine temel oluşturur.

  • Ekle (x) - Tabloya x kaydı ekle> Basit ekleme.
  • Sil (x) - Tablodaki x kaydını sil> Basit sil.
  • Before (x, n) - Sıralanan listedeki x kaydından önceki 'n' kayıtlarını döndür. > X'in değerden daha küçük olduğu ve sonuçlara göre sıralama yaptığı en iyi n sonuçları seçin.

  • Sonra (x, n) - Sıralanan listedeki x kaydının ardından 'n' kayıtlarını döndür. > X'in değerden büyük olduğu ve cümleye göre sıralandığı en iyi n sonuçları seçin.

  • İlk (n) - İlk 'n' kayıtlarını sıralanmış listeden döndür. > En iyi n sonuçları seçin.

  • Son (n) - Sıralanan listeden son 'n' kayıtlarını döndür. > Sipariş sonrasında siparişe göre en üst n sonuçları seçin.

  • Compare (x, y) - Tablodan iki ve iki kayıt verilmişse, x> y olup olmadığını bulun. > TSQL IF bildirimi.

SimpleDB otomatik dizinler, sıralama ve temel bir sorgu dili sağlar . RDBMS seçsem bile sorunum devam edecek. Sorun, veritabanımdaki verilerin sıralamasının keyfi olarak değişmesi ve dizine eklenebilecek (özel bir sıralama sütunu kullanmıyorsam) tek bir özellik olarak yakalanamamasıdır.
chitti

0

İşte her ekleme sonrası Postgres masamı yeniden sıralamak için kullandığım şey:

CREATE OR REPLACE FUNCTION re_rank_list() RETURNS trigger AS $re_rank_list$
DECLARE
    temprow record;
    row_idx integer := 1;    
BEGIN
    FOR temprow IN
    SELECT * FROM your_schema.your_list WHERE list_id = NEW.list_id ORDER BY rank ASC
    LOOP
        UPDATE your_schema.your_list SET rank = row_idx * 100 WHERE id = temprow.id;
        row_idx := row_idx + 1;
    END LOOP;
    RETURN NEW;
END;
$re_rank_list$ LANGUAGE plpgsql;


CREATE TRIGGER re_rank_list AFTER UPDATE ON your_schema.your_list_value
    FOR EACH ROW 
    WHEN (pg_trigger_depth() = 0)
    EXECUTE PROCEDURE re_rank_list();

Kullanım durumum için performans bir endişe değil, hiçbir zaman kırılmayacağına veya garip davranmayacağına güvenmek önemlidir.

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.