Metodoloji ile ilgili olarak, yanlış b-ağacı ;-) havlıyor olduğuna inanıyorum.
Ne biliyoruz:
İlk olarak, durum hakkında bildiklerimizi birleştirip gözden geçirelim:
Ne tahmin edebiliriz:
Daha sonra, bir veya daha fazla şişe boynu bulmamıza ve bir çözüme işaret etmemize veya en azından bazı olası çözümleri ortadan kaldırmamıza yardımcı olacak ek ayrıntıları sentezleyip sentezleyemeyeceğimizi görmek için tüm bu veri noktalarına birlikte bakabiliriz.
Yorumlardaki mevcut düşünce yönü, asıl sorunun SQL Server ve Excel arasında veri aktarımı olmasıdır. Durum gerçekten bu mu? Saklı Yordam 800.000 satırın her biri için çağrılırsa ve her arama için 50 ms sürerse (yani her satır için), bu 40.000 saniyeye (ms değil) kadar ekler. Ve bu 666 dakikaya eşittir (hhmm ;-) veya 11 saatin biraz üzerinde. Yine de tüm sürecin çalışması sadece 7 saat sürdü. Zaten toplam süre içinde 4 saat var ve hatta hesaplamaları yapmak veya sonuçları tekrar SQL Server'a kaydetmek için zaman ekledik. Yani burada bir şey yok.
Saklı Yordamın tanımına bakıldığında, yalnızca bir girdi parametresi vardır @FileID
; üzerinde filtre yok @RowID
. Bu yüzden aşağıdaki iki senaryodan birinin gerçekleştiğinden şüpheleniyorum:
- Bu saklı yordam yok değil aslında ama onun yerine her başı, her satır başına denilen olsun
@FileID
yaklaşık 4000 satır span görünen. Döndürülen 4000 satır oldukça tutarlı bir tutarsa, 800.000 satırda bu gruplardan yalnızca 200 tanesi vardır. Ve her biri 50 ms süren 200 infaz, 7 saatin sadece 10 saniyesine denk geliyor.
- Bu saklı yordam aslında her satır için çağrılırsa, yeni bir satır ilk kez arandığında
@FileID
Tampon Havuzuna yeni satırları çekmek biraz daha uzun sürmez, ancak sonraki 3999 yürütmeleri zaten zaten olduğundan daha hızlı dönecektir önbelleğe alınmış, değil mi?
Bu "filtre" saklı yordam veya SQL Server'dan Excel'e herhangi bir veri aktarımı odaklanmak kırmızı bir ringa balığı olduğunu düşünüyorum .
Şu an için, cansız performansın en alakalı göstergeleri:
- 800.000 satır var
- İşlem her seferinde bir satırda çalışır
- Veri, dolayısıyla "[kullanımları] bazı sütunlarından değerleri SQL Server geri kaydediliyor diğer sütunları işlemek için [my em" faza ayrılmış ;-) dir]
Ondan şüphelendim:
- veri toplama ve hesaplamalarda iyileştirme yapmak için bir miktar alan varken, bunları daha iyi hale getirmek işlem süresinde önemli bir düşüş anlamına gelmez.
- en büyük darboğaz
UPDATE
800.000 ayrı işlem yapıyor, bu da 800.000 ayrı işlem yapıyor.
Tavsiyem (şu anda mevcut bilgilere dayanarak):
En büyük iyileştirme alanınız aynı anda birden fazla satırı güncellemek olacaktır (yani bir işlemde). İşleminizi her biri FileID
yerine her biri açısından çalışacak şekilde güncellemelisiniz RowID
. Yani:
- belirli
FileID
bir dizideki 4000 satırın tamamını bir diziye okur
- dizi, değiştirilen alanları temsil eden öğeler içermelidir
- dizi boyunca dolaşarak her satırı şu anda yaptığınız gibi işleyin
- dizideki tüm satırlar (yani bu özel için
FileID
) hesaplandıktan sonra:
- işlem başlat
- her güncellemeyi her biri için ara
RowID
- hata yoksa işlemi gerçekleştirin
- bir hata oluştuysa, geri al ve uygun şekilde idare et
Kümelenmiş dizininiz zaten olarak tanımlanmamışsa, (FileID, RowID)
bunu dikkate almalısınız (@MikaelEriksson'un Soru üzerine bir yorumda önerdiği gibi). Bu singleton UPDATE'lere yardımcı olmaz, ancak en azından bu temelli filtreleme yordamında yaptığınız gibi toplu işlemleri gerçekleştirir FileID
.
Mantığı derlenmiş bir dile taşımayı düşünmelisiniz. Bir .NET WinForms uygulaması veya hatta Konsol Uygulaması oluşturmanızı öneririm. SQL Agent veya Windows Zamanlanmış Görevler aracılığıyla zamanlaması kolay olduğu için Konsol Uygulamasını tercih ederim. VB.NET veya C # ile yapılması önemli değildir. VB.NET geliştiriciniz için daha doğal bir uyum olabilir, ancak yine de bazı öğrenme eğrileri olacaktır.
Bu noktada SQLCLR'ye geçmek için bir neden göremiyorum. Algoritma sık sık değişirse, bu can sıkıcı olur Meclis her zaman yeniden dağıtmak gerekir. Bir Konsol Uygulamasını yeniden oluşturmak ve .exe'nin ağdaki uygun paylaşılan klasöre yerleştirilmesini sağlamak, böylece aynı programı çalıştırmanız ve her zaman güncel olması, yapması oldukça kolay olmalıdır.
İşlemin tamamen T-SQL'e taşınmasının, sorundan şüphelendiğim şeyse ve sadece bir seferde bir UPDATE yapıyorsanız yardımcı olacağını düşünmüyorum.
İşlem .NET'e taşınırsa, diziyi UPDATE
TVP tablosu değişkenine JOIN'leri çağıracak ve dolayısıyla tek bir işlem olacak bir Saklı Yordam'a geçirecek şekilde Tablo Değerli Parametreler'i (TVP'ler) kullanabilirsiniz. . TVP, INSERT
tek bir işlem halinde gruplandırılmış 4000 s yapmaktan daha hızlı olmalıdır . Ancak, INSERT
1 işlemde 4000 saniyenin üzerindeki TVP'lerin kullanılmasından elde edilen kazanç , 800.000 ayrı işlemden her biri 4000 satırlık yalnızca 200 işleme taşındığında görülen iyileşme kadar önemli olmayacaktır.
TVP seçeneği VBA tarafı için yerel olarak mevcut değildir, ancak birisi test etmeye değer olabilecek bir çözüm bulmuştur:
VBA'dan SQL Server 2008 R2'ye giderken veritabanı performansını nasıl artırabilirim?
Filtre proc yalnızca kullanıyor IF FileID
içinde WHERE
maddesi bu proc gerçekten her satır başına çağrılan IF ve o zaman ilk çalıştırma sonuçlarını önbelleğe ve başına satır kalanı için bunları kullanarak bazı işleme zamandan tasarruf edebilirsiniz FileID
, sağ?
Eğer işlem halletmek kez FileId başına , o zaman biz paralel işlem hakkında konuşmaya başlayabilirsiniz. Ama bu noktada gerekli olmayabilir :). Oldukça büyük 3 ideal olmayan parça ile uğraştığınız göz önüne alındığında: Excel, VBA ve 800k işlemleri, herhangi bir SSIS konuşması veya paralelkenar veya kim bilir, at öncesi öncesi optimizasyon / araba . Bu 7 saatlik işlemi 10 dakika veya daha kısa bir süreye indirebilirsek, daha hızlı hale getirmenin ek yollarını düşünmeye devam eder misiniz? Aklınızda bulunduğunuz bir hedef tamamlama süresi var mı? İşlem her bir FileID'de yapıldıktan sonra unutmayın temel olarak, bir VB.NET Konsol Uygulamanız (yani komut satırı .EXE) olsaydı, SQL Agent CmdExec adımı veya Windows Zamanlanmış Görevleri aracılığıyla, bir kerede bu FileID'lerden birkaçını çalıştırmanızı engelleyen hiçbir şey olmazdı, vb.
VE, her zaman "aşamalı" bir yaklaşım benimseyebilir ve aynı anda birkaç iyileştirme yapabilirsiniz. Örneğin FileID
, grup başına güncellemeler yapmaya başlamak ve dolayısıyla bu grup için bir işlem kullanmak gibi. Ardından, TVP'nin çalışıp çalışmadığını görün. Daha sonra bu kodu alıp VB.NET'e taşımaya bakın (ve TVP'ler .NET'te çalışır, böylece güzel bir şekilde bağlantı kuracaktır).
Bilmediklerimiz hala yardımcı olabilir:
- "Filtre" Prosedür çalıştırmak Saklanan mu SatırKimliği başına veya FileId başına ? Bu Kayıtlı Prosedürün tam tanımına bile sahip miyiz?
- Tablonun tam şeması. Bu masa ne kadar geniş? Kaç tane değişken uzunluk alanı var? Kaç alan NULLable? NULLable varsa, kaç tanesi NULL içerir?
- Bu tablo için dizinler. Bölümlenmiş mi? SATIR veya SAYFA Sıkıştırma kullanılıyor mu?
- Bu tablo MB / GB cinsinden ne kadar büyük?
- Bu tablo için dizin bakımı nasıl ele alınır? Endeksler ne kadar parçalanmış? İstatistikler bugüne kadar nasıl güncelleniyor?
- Bu 7 saatlik süreç devam ederken başka süreçler bu tabloya yazıyor mu? Olası çekişme kaynağı.
- Bu 7 saatlik süreç devam ederken bu tablodan başka süreçler okunuyor mu? Olası çekişme kaynağı.
GÜNCELLEME 1:
** Ne VBA (Uygulamalar için Visual Basic) ve onunla neler yapılabileceği konusunda bir karışıklık var gibi görünüyor, bu yüzden bu hepimizin aynı web sayfasında olduğumuzdan emin olmak için:
GÜNCELLEME 2:
Dikkate alınması gereken bir nokta daha var: Bağlantılar nasıl ele alınıyor? VBA kodu her işlem için Bağlantıyı açar ve kapatır mı, yoksa işlemin başlangıcında bağlantıyı açar ve işlemin sonunda kapatır mı (yani 7 saat sonra)? Bağlantı havuzu oluşturmada bile (varsayılan olarak ADO için etkinleştirilmesi gerekir), 800,200 veya 1,600,000 kez açma ve kapama yerine bir kez açılma ve kapanma arasında hala bir etki olmalıdır. Bu değerler en az 800.000 GÜNCELLEME artı 200 veya 800k EXEC'leri temel alır (filtre saklı yordamın gerçekte ne kadar yürütüldüğüne bağlı olarak).
Çok fazla bağlantı içeren bu sorun, yukarıda özetlediğim öneri ile otomatik olarak azaltılır. Bir işlem oluşturarak ve bu işlem dahilindeki tüm UPDATE'leri yaparak, bu bağlantıyı açık tutacak ve her biri için yeniden kullanacaksınız UPDATE
. Belirtilen kişi başına 4000 satırı almak için bağlantının ilk çağrıdan açık tutulması FileID
ya da bu "get" işleminden sonra kapatılıp UPDATE'ler için tekrar açılmasa da, şimdi bir farktan bahsettiğimizden çok daha az etkilidir. Tüm süreç boyunca 200 veya 400 toplam bağlantı.
GÜNCELLEME 3:
Bazı hızlı testler yaptım. Lütfen bunun aynı işlem değil, oldukça küçük ölçekli bir test olduğunu unutmayın (saf INSERT vs EXEC + UPDATE). Bununla birlikte, bağlantıların ve işlemlerin nasıl ele alındığına ilişkin zamanlamadaki farklılıklar hala geçerlidir, dolayısıyla buradaki bilgiler nispeten benzer bir etkiye sahip olacak şekilde tahmin edilebilir.
Test Parametreleri:
- SQL Server 2012 Developer Edition (64 bit), SP2
Tablo:
CREATE TABLE dbo.ManyInserts
(
RowID INT NOT NULL IDENTITY(1, 1) PRIMARY KEY,
InsertTime DATETIME NOT NULL DEFAULT (GETDATE()),
SomeValue BIGINT NULL
);
Operasyon:
INSERT INTO dbo.ManyInserts (SomeValue) VALUES ({LoopIndex * 12});
- Her test başına toplam kesici uçlar: 10.000
- Her test başına
TRUNCATE TABLE dbo.ManyInserts;
sıfırlama : (bu testin niteliği göz önüne alındığında, FREEPROCCACHE, FREESYSTEMCACHE ve DROPCLEANBUFFERS çok fazla değer katmadı.)
- Kurtarma Modeli: BASİT (ve Günlük dosyasında belki 1 GB ücretsiz)
- İşlemler kullanan sınamalar, kaç İşlemden bağımsız olarak yalnızca tek bir Bağlantı kullanır.
Sonuçlar:
Test Milliseconds
------- ------------
10k INSERTs across 10k Connections 3968 - 4163
10k INSERTs across 1 Connection 3466 - 3654
10k INSERTs across 1 Transaction 1074 - 1086
10k INSERTs across 10 Transactions 1095 - 1169
Gördüğünüz gibi, DB'ye ADO bağlantısı zaten tüm işlemlerde paylaşılsa bile, bunları açık bir işlem kullanarak (ADO nesnesi bunu işleyebilmelidir) önemli ölçüde (yani 2 katın üzerinde iyileştirme) kullanarak gruplara ayırma toplam işlem süresini azaltır.