Kita perlu melakukan beberapa pelaporan setiap malam di SQL Server 2008 R2 kami. Menghitung laporan membutuhkan beberapa jam. Untuk mempersingkat waktu kita menghitung ulang tabel. Tabel ini dibuat berdasarkan tabel JOINining 12 cukup besar (puluhan juta baris).
Perhitungan tabel agregasi ini memakan waktu hingga beberapa hari yang lalu karena 4 jam. DBA kami kemudian membagi gabungan besar ini menjadi 3 gabungan lebih kecil (masing-masing bergabung dengan 4 tabel). Hasil sementara disimpan ke dalam tabel sementara setiap waktu, yang digunakan di gabung berikutnya.
Hasil peningkatan DBA adalah, bahwa tabel agregasi dihitung dalam 15 menit. Saya bertanya-tanya bagaimana itu mungkin. DBA mengatakan kepada saya bahwa itu karena jumlah data yang harus diproses server lebih kecil. Dengan kata lain, bahwa dalam gabung asli yang besar, server harus bekerja dengan lebih banyak data daripada jumlah yang lebih kecil. Namun, saya menganggap pengoptimal akan melakukannya secara efisien dengan gabung besar asli, memisahkan gabung sendiri dan hanya mengirim jumlah kolom yang diperlukan untuk gabung berikutnya.
Hal lain yang dia lakukan adalah dia membuat indeks di salah satu tabel sementara. Namun, sekali lagi saya akan berpikir bahwa pengoptimal akan membuat tabel hash yang sesuai jika diperlukan dan semuanya lebih baik mengoptimalkan perhitungan.
Saya membicarakan hal ini dengan DBA kami, tetapi dia sendiri tidak yakin tentang apa yang menyebabkan peningkatan dalam waktu pemrosesan. Dia hanya menyebutkan, bahwa dia tidak akan menyalahkan server karena dapat berlebihan untuk menghitung data sebesar itu dan bahwa mungkin pengoptimal memiliki waktu yang sulit untuk memprediksi rencana pelaksanaan terbaik .... Ini saya mengerti, tetapi saya ingin memiliki jawaban yang lebih jelas tentang mengapa persisnya.
Jadi, pertanyaannya adalah:
Apa yang mungkin menyebabkan peningkatan besar?
Apakah ini prosedur standar untuk membagi sambungan besar menjadi lebih kecil?
Apakah jumlah data yang server harus proses benar-benar lebih kecil untuk beberapa gabungan yang lebih kecil?
Ini query aslinya:
Insert Into FinalResult_Base
SELECT
TC.TestCampaignContainerId,
TC.CategoryId As TestCampaignCategoryId,
TC.Grade,
TC.TestCampaignId,
T.TestSetId
,TL.TestId
,TSK.CategoryId
,TT.[TestletId]
,TL.SectionNo
,TL.Difficulty
,TestletName = Char(65+TL.SectionNo) + CONVERT(varchar(4),6 - TL.Difficulty)
,TQ.[QuestionId]
,TS.StudentId
,TS.ClassId
,RA.SubjectId
,TQ.[QuestionPoints]
,GoodAnswer = Case When TQ.[QuestionPoints] Is null Then 0
When TQ.[QuestionPoints] > 0 Then 1
Else 0 End
,WrongAnswer = Case When TQ.[QuestionPoints] = 0 Then 1
When TQ.[QuestionPoints] Is null Then 1
Else 0 End
,NoAnswer = Case When TQ.[QuestionPoints] Is null Then 1 Else 0 End
,TS.Redizo
,TT.ViewCount
,TT.SpentTime
,TQ.[Position]
,RA.SpecialNeeds
,[Version] = 1
,TestAdaptationId = TA.Id
,TaskId = TSK.TaskId
,TaskPosition = TT.Position
,QuestionRate = Q.Rate
,TestQuestionId = TQ.Guid
,AnswerType = TT.TestletAnswerTypeId
FROM
[TestQuestion] TQ WITH (NOLOCK)
Join [TestTask] TT WITH (NOLOCK) On TT.Guid = TQ.TestTaskId
Join [Question] Q WITH (NOLOCK) On TQ.QuestionId = Q.QuestionId
Join [Testlet] TL WITH (NOLOCK) On TT.TestletId = TL.Guid
Join [Test] T WITH (NOLOCK) On TL.TestId = T.Guid
Join [TestSet] TS WITH (NOLOCK) On T.TestSetId = TS.Guid
Join [RoleAssignment] RA WITH (NOLOCK) On TS.StudentId = RA.PersonId And RA.RoleId = 1
Join [Task] TSK WITH (NOLOCK) On TSK.TaskId = TT.TaskId
Join [Category] C WITH (NOLOCK) On C.CategoryId = TSK.CategoryId
Join [TimeWindow] TW WITH (NOLOCK) On TW.Id = TS.TimeWindowId
Join [TestAdaptation] TA WITH (NOLOCK) On TA.Id = TW.TestAdaptationId
Join [TestCampaign] TC WITH (NOLOCK) On TC.TestCampaignId = TA.TestCampaignId
WHERE
T.TestTypeId = 1 -- eliminuji ankety
And t.ProcessedOn is not null -- ne vsechny, jen dokoncene
And TL.ShownOn is not null
And TS.Redizo not in (999999999, 111111119)
END;
Dipecah baru bergabung setelah karya besar DBA:
SELECT
TC.TestCampaignContainerId,
TC.CategoryId As TestCampaignCategoryId,
TC.Grade,
TC.TestCampaignId,
T.TestSetId
,TL.TestId
,TL.SectionNo
,TL.Difficulty
,TestletName = Char(65+TL.SectionNo) + CONVERT(varchar(4),6 - TL.Difficulty) -- prevod na A5, B4, B5 ...
,TS.StudentId
,TS.ClassId
,TS.Redizo
,[Version] = 1 -- ?
,TestAdaptationId = TA.Id
,TL.Guid AS TLGuid
,TS.TimeWindowId
INTO
[#FinalResult_Base_1]
FROM
[TestSet] [TS] WITH (NOLOCK)
JOIN [Test] [T] WITH (NOLOCK)
ON [T].[TestSetId] = [TS].[Guid] AND [TS].[Redizo] NOT IN (999999999, 111111119) AND [T].[TestTypeId] = 1 AND [T].[ProcessedOn] IS NOT NULL
JOIN [Testlet] [TL] WITH (NOLOCK)
ON [TL].[TestId] = [T].[Guid] AND [TL].[ShownOn] IS NOT NULL
JOIN [TimeWindow] [TW] WITH (NOLOCK)
ON [TW].[Id] = [TS].[TimeWindowId] AND [TW].[IsActive] = 1
JOIN [TestAdaptation] [TA] WITH (NOLOCK)
ON [TA].[Id] = [TW].[TestAdaptationId] AND [TA].[IsActive] = 1
JOIN [TestCampaign] [TC] WITH (NOLOCK)
ON [TC].[TestCampaignId] = [TA].[TestCampaignId] AND [TC].[IsActive] = 1
JOIN [TestCampaignContainer] [TCC] WITH (NOLOCK)
ON [TCC].[TestCampaignContainerId] = [TC].[TestCampaignContainerId] AND [TCC].[IsActive] = 1
;
SELECT
FR1.TestCampaignContainerId,
FR1.TestCampaignCategoryId,
FR1.Grade,
FR1.TestCampaignId,
FR1.TestSetId
,FR1.TestId
,TSK.CategoryId AS [TaskCategoryId]
,TT.[TestletId]
,FR1.SectionNo
,FR1.Difficulty
,TestletName = Char(65+FR1.SectionNo) + CONVERT(varchar(4),6 - FR1.Difficulty) -- prevod na A5, B4, B5 ...
,FR1.StudentId
,FR1.ClassId
,FR1.Redizo
,TT.ViewCount
,TT.SpentTime
,[Version] = 1 -- ?
,FR1.TestAdaptationId
,TaskId = TSK.TaskId
,TaskPosition = TT.Position
,AnswerType = TT.TestletAnswerTypeId
,TT.Guid AS TTGuid
INTO
[#FinalResult_Base_2]
FROM
#FinalResult_Base_1 FR1
JOIN [TestTask] [TT] WITH (NOLOCK)
ON [TT].[TestletId] = [FR1].[TLGuid]
JOIN [Task] [TSK] WITH (NOLOCK)
ON [TSK].[TaskId] = [TT].[TaskId] AND [TSK].[IsActive] = 1
JOIN [Category] [C] WITH (NOLOCK)
ON [C].[CategoryId] = [TSK].[CategoryId]AND [C].[IsActive] = 1
;
DROP TABLE [#FinalResult_Base_1]
CREATE NONCLUSTERED INDEX [#IX_FR_Student_Class]
ON [dbo].[#FinalResult_Base_2] ([StudentId],[ClassId])
INCLUDE ([TTGuid])
SELECT
FR2.TestCampaignContainerId,
FR2.TestCampaignCategoryId,
FR2.Grade,
FR2.TestCampaignId,
FR2.TestSetId
,FR2.TestId
,FR2.[TaskCategoryId]
,FR2.[TestletId]
,FR2.SectionNo
,FR2.Difficulty
,FR2.TestletName
,TQ.[QuestionId]
,FR2.StudentId
,FR2.ClassId
,RA.SubjectId
,TQ.[QuestionPoints] -- 1+ good, 0 wrong, null no answer
,GoodAnswer = Case When TQ.[QuestionPoints] Is null Then 0
When TQ.[QuestionPoints] > 0 Then 1 -- cookie
Else 0 End
,WrongAnswer = Case When TQ.[QuestionPoints] = 0 Then 1
When TQ.[QuestionPoints] Is null Then 1
Else 0 End
,NoAnswer = Case When TQ.[QuestionPoints] Is null Then 1 Else 0 End
,FR2.Redizo
,FR2.ViewCount
,FR2.SpentTime
,TQ.[Position] AS [QuestionPosition]
,RA.SpecialNeeds -- identifikace SVP
,[Version] = 1 -- ?
,FR2.TestAdaptationId
,FR2.TaskId
,FR2.TaskPosition
,QuestionRate = Q.Rate
,TestQuestionId = TQ.Guid
,FR2.AnswerType
INTO
[#FinalResult_Base]
FROM
[#FinalResult_Base_2] FR2
JOIN [TestQuestion] [TQ] WITH (NOLOCK)
ON [TQ].[TestTaskId] = [FR2].[TTGuid]
JOIN [Question] [Q] WITH (NOLOCK)
ON [Q].[QuestionId] = [TQ].[QuestionId] AND [Q].[IsActive] = 1
JOIN [RoleAssignment] [RA] WITH (NOLOCK)
ON [RA].[PersonId] = [FR2].[StudentId]
AND [RA].[ClassId] = [FR2].[ClassId] AND [RA].[IsActive] = 1 AND [RA].[RoleId] = 1
drop table #FinalResult_Base_2;
truncate table [dbo].[FinalResult_Base];
insert into [dbo].[FinalResult_Base] select * from #FinalResult_Base;
drop table #FinalResult_Base;
sumber
READCOMMITTED
? Saya belum pernah melihat ROWCOMMITTED sebelumnya.Jawaban:
1 Pengurangan 'ruang pencarian', ditambah dengan statistik yang lebih baik untuk gabungan antara / akhir.
Saya harus berurusan dengan 90-tabel bergabung (desain mickey mouse) di mana Prosesor Kueri bahkan menolak untuk membuat rencana. Memecah gabungan seperti itu menjadi 10 submoins dari masing-masing 9 tabel, secara dramatis menurunkan kompleksitas setiap sambungan, yang tumbuh secara eksponensial dengan setiap tabel tambahan. Ditambah Pengoptimal Permintaan sekarang menganggapnya sebagai 10 paket, menghabiskan (berpotensi) lebih banyak waktu secara keseluruhan (Paul White bahkan mungkin memiliki metrik!).
Tabel hasil antara sekarang akan memiliki statistik baru sendiri, sehingga bergabung jauh lebih baik dibandingkan dengan statistik pohon yang dalam yang menjadi miring awal dan berakhir sebagai Fiksi Ilmiah segera sesudahnya.
Plus, Anda dapat memaksa gabungan paling selektif terlebih dahulu, mengurangi volume data yang bergerak ke atas pohon. Jika Anda dapat memperkirakan selektivitas predikat Anda jauh lebih baik daripada Pengoptimal, mengapa tidak memaksakan urutan bergabung. Mungkin layak mencari "Rencana Lebat".
2 Ini harus dipertimbangkan dalam pandangan saya, jika efisiensi dan kinerja penting
3 Tidak harus, tetapi bisa juga jika gabungan yang paling selektif dijalankan sejak awal
sumber
sumber
Baiklah, izinkan saya mulai dengan mengatakan bahwa Anda mengerjakan data kecil - 10ns juta tidak besar. Projet DWH terakhir yang saya miliki memiliki 400 juta baris ditambahkan ke tabel fakta. PER HARI. Penyimpanan selama 5 tahun.
Masalahnya adalah perangkat keras, sebagian. Karena sambungan besar dapat menggunakan BANYAK ruang sementara dan hanya ada begitu banyak RAM, saat Anda melimpah ke disk, hal itu menjadi jauh lebih lambat. Dengan demikian, mungkin masuk akal untuk membagi pekerjaan menjadi bagian-bagian yang lebih kecil hanya karena sementara SQL hidup di dunia set, dan tidak peduli tentang ukuran, server yang Anda jalankan tidak terbatas. Saya cukup terbiasa untuk keluar dari kesalahan ruang dalam 64 GB tempdb selama beberapa operasi.
Jika tidak, selama staitsics teratur, pengoptimal kueri tidak kewalahan. Tidak peduli seberapa besar tabelnya - ia bekerja dengan statistik yang benar-benar tidak tumbuh. BILANG BAHWA: Jika Anda benar-benar memiliki tabel BESAR (digit dua digit jumlah baris) maka mereka mungkin sedikit kasar.
Ada juga masalah penguncian - kecuali jika Anda memprogram bahwa sambungan besar dapat mengunci meja selama berjam-jam. Saya sedang melakukan operasi penyalinan 200gb saat ini, dan saya membaginya menjadi lebih kecil dengan kunci bisnis (perulangan efektif) yang membuat kunci jauh lebih pendek.
Pada akhirnya, kami bekerja dengan perangkat keras yang terbatas.
sumber