Berkenaan dengan metodologi, saya percaya Anda menggonggong b-tree yang salah ;-).
Apa yang kita ketahui:
Pertama, mari berkonsolidasi dan tinjau apa yang kita ketahui tentang situasi ini:
Apa yang bisa kita duga:
Selanjutnya, kita dapat melihat semua titik data ini bersama-sama untuk melihat apakah kita dapat mensintesis detail tambahan yang akan membantu kita menemukan satu atau lebih leher botol, dan menunjuk pada suatu solusi, atau setidaknya mengesampingkan beberapa solusi yang mungkin keluar.
Arah pemikiran saat ini dalam komentar adalah bahwa masalah utama adalah transfer data antara SQL Server dan Excel. Apakah benar hal itu merupakan masalahnya? Jika Prosedur Tersimpan dipanggil untuk masing-masing 800.000 baris dan membutuhkan 50 ms per setiap panggilan (yaitu per setiap baris), itu menambah hingga 40.000 detik (bukan ms). Dan itu setara dengan 666 menit (hhmm ;-), atau hanya lebih dari 11 jam. Namun seluruh proses dikatakan hanya memakan waktu 7 jam untuk berjalan. Kami sudah 4 jam dari total waktu, dan kami bahkan telah menambahkan waktu untuk melakukan perhitungan atau menyimpan hasilnya kembali ke SQL Server. Jadi ada sesuatu yang tidak beres di sini.
Melihat definisi Prosedur Tersimpan, hanya ada parameter input untuk @FileID
; tidak ada filter aktif @RowID
. Jadi saya menduga bahwa salah satu dari dua skenario berikut sedang terjadi:
- Prosedur Tersimpan ini tidak benar - benar dipanggil per setiap baris, tetapi sebaliknya per masing-masing
@FileID
, yang tampaknya menjangkau sekitar 4000 baris. Jika 4000 baris yang dikembalikan adalah jumlah yang cukup konsisten, maka hanya ada 200 dari pengelompokan dalam 800.000 baris. Dan 200 eksekusi mengambil 50 ms setiap jumlah hanya 10 detik dari 7 jam itu.
- Jika prosedur yang tersimpan ini benar-benar dipanggil untuk setiap baris, maka bukankah pertama kali sebuah baru
@FileID
dilewatkan akan membutuhkan waktu sedikit lebih lama untuk menarik baris baru ke dalam Buffer Pool, tetapi kemudian 3999 eksekusi berikutnya biasanya akan kembali lebih cepat karena sudah menjadi di-cache, kan?
Saya pikir fokus pada Prosedur Tersimpan "filter" ini, atau transfer data apa pun dari SQL Server ke Excel, adalah herring merah .
Untuk saat ini, saya pikir indikator yang paling relevan dari kinerja loyo adalah:
- Ada 800.000 baris
- Operasi bekerja pada satu baris sekaligus
- Data disimpan kembali ke SQL Server, karenanya "[menggunakan] nilai dari beberapa kolom untuk memanipulasi kolom lain " [my em phas is ;-)]
Saya menduga bahwa:
- sementara ada beberapa ruang untuk perbaikan pada pengambilan data dan perhitungan, menjadikannya lebih baik tidak akan berarti pengurangan yang signifikan dalam waktu pemrosesan.
- kemacetan utama adalah mengeluarkan 800.000
UPDATE
pernyataan terpisah , yang merupakan 800.000 transaksi terpisah.
Rekomendasi saya (berdasarkan informasi yang tersedia saat ini):
Bidang peningkatan terbesar Anda adalah memperbarui beberapa baris sekaligus (yaitu dalam satu transaksi). Anda harus memperbarui proses Anda agar berfungsi dalam hal masing-masing FileID
alih-alih masing-masing RowID
. Begitu:
- baca di semua 4000 baris tertentu
FileID
ke dalam array
- array harus mengandung elemen yang mewakili bidang yang sedang dimanipulasi
- siklus melalui array, memproses setiap baris seperti yang Anda lakukan saat ini
- setelah semua baris dalam array (yaitu untuk ini
FileID
) telah dihitung:
- memulai transaksi
- panggil setiap pembaruan per masing-masing
RowID
- jika tidak ada kesalahan, lakukan transaksi
- jika terjadi kesalahan, kembalikan dan tangani dengan tepat
Jika indeks cluster Anda belum didefinisikan (FileID, RowID)
maka Anda harus mempertimbangkan itu (seperti yang disarankan @MikaelEriksson dalam komentar pada Pertanyaan). Ini tidak akan membantu UPDATE tunggal ini, tetapi setidaknya akan sedikit meningkatkan operasi agregat, seperti apa yang Anda lakukan dalam prosedur tersimpan "filter" karena semuanya didasarkan pada FileID
.
Anda harus mempertimbangkan untuk memindahkan logika ke bahasa yang dikompilasi. Saya akan menyarankan membuat aplikasi .NET WinForms atau bahkan Aplikasi Konsol. Saya lebih suka Aplikasi Konsol karena mudah menjadwalkan melalui SQL Agent atau Tugas Terjadwal Windows. Seharusnya tidak masalah apakah itu dilakukan dalam VB.NET atau C #. VB.NET mungkin lebih cocok untuk pengembang Anda, tetapi masih ada beberapa kurva belajar.
Saya tidak melihat alasan pada saat ini untuk pindah ke SQLCLR. Jika algoritma sering berubah, itu akan mengganggu harus menggunakan kembali Majelis sepanjang waktu. Membangun kembali aplikasi konsol dan menempatkan .exe ditempatkan di folder bersama yang tepat di jaringan sehingga Anda hanya menjalankan program yang sama dan kebetulan selalu up-to-date, harus cukup mudah dilakukan.
Saya tidak berpikir memindahkan pemrosesan sepenuhnya ke T-SQL akan membantu jika masalahnya adalah apa yang saya duga dan Anda hanya melakukan satu PEMBARUAN sekaligus.
Jika pemrosesan dipindahkan ke .NET, Anda kemudian dapat menggunakan Table-Valued Parameters (TVPs) sehingga Anda akan meneruskan array ke Prosedur Tersimpan yang akan memanggil seorang UPDATE
yang BERGABUNG ke variabel tabel TVP dan karenanya merupakan satu transaksi . TVP harus lebih cepat daripada melakukan 4000 INSERT
yang dikelompokkan ke dalam satu transaksi. Tetapi keuntungan yang didapat dari menggunakan TVPs lebih dari 4000 INSERT
detik dalam 1 transaksi kemungkinan tidak akan sama pentingnya dengan peningkatan yang terlihat ketika beralih dari 800.000 transaksi terpisah menjadi hanya 200 transaksi masing-masing dari 4000 baris.
Opsi TVP tidak tersedia secara native untuk sisi VBA, tetapi seseorang datang dengan solusi yang mungkin layak untuk diuji:
Bagaimana cara meningkatkan kinerja basis data saat beralih dari VBA ke SQL Server 2008 R2?
JIKA proc filter hanya menggunakan FileID
dalam WHERE
klausa, dan JIKA proc itu benar-benar dipanggil per setiap baris, maka Anda dapat menghemat waktu pemrosesan dengan cache hasil run pertama dan menggunakannya untuk sisa baris per itu FileID
, Baik?
Setelah Anda menyelesaikan pemrosesan per FileID , maka kita dapat mulai berbicara tentang pemrosesan paralel. Tapi itu mungkin tidak perlu pada saat itu :). Mengingat bahwa Anda berurusan dengan 3 bagian non-ideal yang cukup besar: transaksi Excel, VBA, dan 800 ribu, pembicaraan SSIS, atau jajaran genjang, atau siapa yang tahu apa, adalah pengoptimalan dini / jenis barang sebelum kuda . Jika kita bisa mendapatkan proses 7 jam ini menjadi 10 menit atau kurang, apakah Anda masih memikirkan cara lain untuk membuatnya lebih cepat? Apakah ada target waktu penyelesaian yang Anda pikirkan? Perlu diingat bahwa setelah pemrosesan dilakukan pada per FileID dasar, jika Anda memiliki VB.NET Console App (yaitu command-line .EXE), tidak akan ada yang menghentikan Anda dari menjalankan beberapa FileID tersebut sekaligus :), baik melalui langkah SQL Agent CmdExec atau Windows Scheduled Tasks, dll.
DAN, Anda selalu dapat mengambil pendekatan "bertahap" dan melakukan beberapa peningkatan sekaligus. Seperti memulai dengan melakukan pembaruan per FileID
dan karenanya menggunakan satu transaksi untuk grup itu. Kemudian, lihat apakah Anda bisa membuat TVP berfungsi. Kemudian lihat tentang mengambil kode itu dan memindahkannya ke VB.NET (dan TVPs bekerja di .NET sehingga akan port dengan baik).
Apa yang kita tidak tahu yang masih bisa membantu:
- Apakah "Stored" Stored Procedure berjalan per RowID atau per FileID ? Apakah kita bahkan memiliki definisi penuh tentang Prosedur Tersimpan itu?
- Skema penuh dari tabel. Seberapa lebar tabel ini? Berapa banyak bidang panjang variabel? Berapa banyak bidang yang NULLable? Jika ada yang NULLable, berapa banyak yang mengandung NULLs?
- Indeks untuk tabel ini. Apakah dipartisi? Apakah ROW atau PAGE Compression digunakan?
- Seberapa besar tabel ini dalam hal MB / GB?
- Bagaimana pemeliharaan indeks ditangani untuk tabel ini? Seberapa terfragmentasi indeks? Bagaimana statistik terkini diperbarui?
- Apakah ada proses lain menulis ke tabel ini saat proses 7 jam ini berlangsung? Kemungkinan sumber pertikaian.
- Apakah ada proses lain yang dibaca dari tabel ini saat proses 7 jam ini berlangsung? Kemungkinan sumber pertikaian.
PEMBARUAN 1:
** Tampaknya ada beberapa kebingungan tentang apa yang VBA (Visual Basic for Applications) dan apa yang dapat dilakukan dengannya, jadi ini hanya untuk memastikan kita semua berada di halaman web yang sama:
PEMBARUAN 2:
Satu hal lagi yang perlu dipertimbangkan: Bagaimana koneksi ditangani? Apakah kode VBA membuka dan menutup Koneksi per setiap operasi, atau apakah itu membuka koneksi pada awal proses dan menutupnya pada akhir proses (yaitu 7 jam kemudian)? Bahkan dengan penyatuan koneksi (yang, secara default, harus diaktifkan untuk ADO), masih harus ada dampak yang cukup antara membuka dan menutup sekali sebagai lawan membuka dan menutup baik 800.200 atau 1.600.000 kali. Nilai-nilai tersebut didasarkan pada setidaknya 800.000 UPDATE ditambah 200 atau 800k EXEC (tergantung pada seberapa sering prosedur yang tersimpan filter sebenarnya dieksekusi).
Masalah terlalu banyak koneksi ini secara otomatis dikurangi dengan rekomendasi yang saya uraikan di atas. Dengan membuat transaksi dan melakukan semua UPDATE dalam transaksi itu, Anda akan menjaga agar koneksi tetap terbuka dan menggunakannya kembali untuk masing-masingnya UPDATE
. Apakah koneksi tetap terbuka dari panggilan awal untuk mendapatkan 4000 baris per yang ditentukan FileID
, atau ditutup setelah operasi "dapatkan" dan dibuka lagi untuk UPDATE, jauh lebih tidak berdampak karena kita sekarang berbicara tentang perbedaan antara keduanya 200 atau 400 total koneksi di seluruh proses.
PEMBARUAN 3:
Saya melakukan beberapa pengujian cepat. Perlu diingat bahwa ini adalah tes skala yang agak kecil, dan bukan operasi yang sama persis (INSERT murni vs EXEC + PEMBARUAN). Namun, perbedaan waktu terkait dengan bagaimana koneksi dan transaksi ditangani masih relevan, maka informasi tersebut dapat diekstrapolasi untuk memiliki dampak yang relatif sama di sini.
Parameter uji:
- Edisi Pengembang SQL Server 2012 (64-bit), SP2
Meja:
CREATE TABLE dbo.ManyInserts
(
RowID INT NOT NULL IDENTITY(1, 1) PRIMARY KEY,
InsertTime DATETIME NOT NULL DEFAULT (GETDATE()),
SomeValue BIGINT NULL
);
Operasi:
INSERT INTO dbo.ManyInserts (SomeValue) VALUES ({LoopIndex * 12});
- Total Sisipan per setiap tes: 10.000
- Reset per setiap tes:
TRUNCATE TABLE dbo.ManyInserts;
(mengingat sifat dari tes ini, melakukan FREEPROCCACHE, FREESYSTEMCACHE, dan DROPCLEANBUFFERS tampaknya tidak menambah banyak nilai.)
- Model Pemulihan: SIMPLE (dan mungkin 1 GB gratis di file Log)
- Tes yang menggunakan Transaksi hanya menggunakan Koneksi tunggal terlepas dari berapa banyak Transaksi.
Hasil:
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
Seperti yang Anda lihat, bahkan jika koneksi ADO ke DB sudah dibagikan di semua operasi, pengelompokan mereka ke dalam batch menggunakan transaksi eksplisit (objek ADO harus dapat menangani ini) dijamin secara signifikan (yaitu lebih dari 2x peningkatan) mengurangi waktu proses keseluruhan.