Karmaşık Ölçütlerle Dizinlenmiş Okumaları En Aza İndirme


12

Firebird 2.5 çalışma bileti veritabanını optimize ediyorum. Bu şekilde beyan edilen bir tabloda saklanırlar:

CREATE TABLE TICKETS (
  TICKET_ID id PRIMARY KEY,
  JOB_ID id,
  ACTION_ID id,
  STATUS str256 DEFAULT 'Pending'
);

Genel olarak işlenmemiş ve Pendingstatüsünde olan ilk bileti bulmak istiyorum .

Benim işlem döngüsü şöyle olurdu:

  1. 1. Bileti Al nerede Pending
  2. Bilet ile çalışın.
  3. Bilet Durumunu Güncelle => Complete
  4. Tekrar et.

Çok süslü bir şey yok. Bu döngü çalışırken veritabanını izliyorsam, her yineleme için dizinli okuma tırmanışlarının sayısını görüyorum. Performansın anlayabildiğim kadar düştüğü görülmüyor, ancak test ettiğim makine oldukça hızlı. Ancak, bazı kullanıcılarımdan zaman içinde performans düşüşü raporları aldım.

Bir dizin var Status, ama yine de Ticket_Idher yineleme sütun aşağı tarar gibi görünüyor . Görünüşe göre bir şeye bakmıyorum, ama ne olduğundan emin değilim. Bunun gibi bir şey için indeksli okumaların tırmanma sayısı bekleniyor mu, yoksa endeks bir şekilde yanlış mı davranıyor?

- Yorumlar için düzenlemeler -

Firebird'de satır alımını şu şekilde sınırlandırırsınız:

Select First 1
  Job_ID, Ticket_Id
From
  Tickets
Where
  Status = 'Pending'

"İlk" dediğimde, sadece sınırlı bir kayıt kümesi istedim Status = 'Pending'.


Birlikte ne anlama geliyor "ilk" in "1 Bilet Al nerede 'Bekleyen'" ?
ypercubeᵀᴹ

"İlk" en küçük ticket_iddemekse, muhtemelen bir dizine ihtiyacınız var(status, ticket_id)
ypercubeᵀᴹ

Performans düşüşünün diğer sorgular / ifadelerden değil, bu prosedürden kaynaklandığından nasıl eminsiniz?
ypercubeᵀᴹ

@ypercube - Hayır, performans düşüşünün nerede olduğundan emin değilim. Bu yüzden sorum "bununla ilgilenmem gerekiyor mu, yoksa bir indeksin normal davranışı mı?" Veritabanını izlerken fark ettiğim bir şeydi ve beklenmedik olduğunu düşündüm. Ben dizinlenmiş bir sütuna karşı nerede yan tümce sağlamak önceki satırları taramaya devam beklemem. FWIW, dizini ticket_idaslında Durum dizinine sahip olmaktan daha kötü performans içerecek şekilde değiştirir .
gddc

id(veri türü) Eğer tanımlanan bir alanı?
a_horse_with_no_name

Yanıtlar:


1

Zaman içindeki bozulma, "Tamamlandı" durumunda olan öğe sayısının artması nedeniyle oluşur. Bunu bir saniyeliğine düşünün - muhtemelen "Tamamlandı" statüsünde az sayıda satıra sahip olduğunuz için test yaparken herhangi bir performans düşüşü yaşamayacaksınız. Ancak üretimde, "Tam" statüsüne sahip milyonlarca satırları olabilir ve bu sayı zamanla artacaktır. Bu, temelde, Durum endeksinizi zamanla gittikçe daha az kullanışlı hale getirir. Bu nedenle, veritabanı büyük olasılıkla Durum'un neredeyse her zaman 'Tamamlandı' değerine sahip olduğundan, yalnızca dizini kullanmak yerine tabloyu tarayacağına karar verir.

SQL Server'da (ve belki de diğer RDBMS'lerde?), Filtrelenmiş Dizinler kullanılarak bu geçici çözüm kullanılabilir. SQL Server'da dizin tanımınızın sonuna "bu dizini yalnızca <> 'Tamamlandı" durumuna sahip kayıtlara uygula "demek için bir WHERE koşulu eklersiniz. Daha sonra bu yüklemi kullanan herhangi bir sorgu, büyük olasılıkla 'Tamamlandı' olarak ayarlanmamış küçük kayıtlardaki dizini kullanır. Ancak, buradaki belgelere dayanarak: http://www.firebirdsql.org/refdocs/langrefupd25-ddl-index.html , Firebird filtrelenmiş dizinleri desteklemiyor gibi görünmüyor.

Geçici çözüm, 'Arşivler' tablosuna 'Tamamlandı' kayıtları koymaktır. Bilet tablonuzla tam olarak aynı tanıma sahip bir tablo oluşturun (otomatik olarak oluşturulmuş herhangi bir kimlik olmasa da) ve ArchiveTickets tablosuna 'Complete' kayıtlarını basarak aralarındaki satırları koruyun. Bilet tablonuzdaki Dizin çok daha az sayıda kaydın üzerinde olacak ve çok daha yüksek performans olacaktır. Bu, büyük olasılıkla Arşiv tablosuna işaret etmek veya Biletler ve ArchiveTickets'te bir UNION gerçekleştirmek için 'Tamamlandı' biletlere referans veren raporları vb. Değiştirmeniz gerektiği anlamına gelecektir. Bu sadece hızlı olmakla kalmayıp aynı zamanda diğer sorgularda daha iyi performans göstermesi için ArchiveTickets tablosu için belirli dizinler oluşturabileceğiniz anlamına da gelir (örneğin:

Üretiminiz binlerce satıra gidecekse bununla ilgilenmelisiniz. Performans zamanla azalır ve kullanıcı deneyiminizi olumsuz etkiler.


0

Performansın etkilenip etkilenmeyeceği, veri hacmi ve makine kapasitesinin bir işlevi olacaktır. Modern donanımın kapasitesi göz önüne alındığında, tanımladığınız tasarım tarafından ele alınamayan bilet satış hacmini hayal etmek zor. Bununla birlikte, doğruluk için önereceğim değişiklikler var ve ikincil fayda olarak performansı artırabilir.

Sizin beklemedeki ilk olsun sorgusu belirli olmayan olduğunu. İlk olarak hangi sıraya göre? Bir SQL tablosunun gerçek sırası yoktur; First 1kesmek sadece seni veriyor bazı keyfi birincisi. Bunu deterministik yapmak için, bekleyen işleri Job_ID sırasında neden işlemeyesiniz?

{Job_ID} ve {Status, Job_ID} olmak üzere iki dizininiz varsa, bu sorgu öngörülebilir ve verimli bir şekilde bir satır döndürür:

Select Job_ID, Ticket_Id
From   Tickets
Where Job_ID = ( 
  select min(Job_ID) from Tickets 
  where Status = 'Pending'
);

Ben bir Firebird kullanıcısı değilim, bu yüzden sorgu planını kontrol etmek zorunda kalacaksınız, ancak alt sorgu sadece ikinci dizine referans verdiğinden , etkili olması gerekir , ilki için bir değer üretir. (Kullanabileceğiniz başka verimlilik hileleri olabilir. Fiziksel tabloyu bir B + ağacı olarak düzenleyebilir veya örneğin gizli bir row_id'e erişebilirsiniz.)

Doğruluk için yapacağım diğer değişiklik, Statustek bir kısıtlı bayt yapmak ve uygulamanın "Beklemede" dizesini sağlamasına izin vermektir. Bu, hatalı Statusdeğerlere karşı koruma sağlayacak ve muhtemelen endeksi pazarlıkta daha küçük hale getirecektir. Gibi bir şey:

CREATE TABLE TICKETS (
  TICKET_ID id PRIMARY KEY,
  JOB_ID id,
  ACTION_ID id,
  STATUS char(1) not NULL 
     DEFAULT 'P'
     CHECK( STATUS in ('P', 'C', 'X') ) -- whatever the domain is
);

Tabii ki, Durum için kanonik dizeler sağlamak için bir görünüm (veya belki türetilmiş bir sütun) kullanabilirsiniz.

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.