Đây là phần thứ ba trong loạt bài gồm năm phần đi sâu vào cách các kế hoạch song song của chế độ hàng SQL Server bắt đầu thực thi. Phần 1 khởi tạo ngữ cảnh thực thi bằng 0 cho tác vụ mẹ và phần 2 tạo cây quét truy vấn. Bây giờ chúng tôi đã sẵn sàng để bắt đầu quét truy vấn, thực hiện một số giai đoạn đầu xử lý và bắt đầu các tác vụ song song bổ sung đầu tiên.
Bắt đầu quét truy vấn
Nhắc lại rằng chỉ có tác vụ chính tồn tại ngay bây giờ và các sàn giao dịch (nhà khai thác song song) chỉ có một bên là người tiêu dùng. Tuy nhiên, điều này là đủ để bắt đầu thực thi truy vấn trên chuỗi công nhân của tác vụ mẹ. Bộ xử lý truy vấn bắt đầu thực thi bằng cách bắt đầu quá trình quét truy vấn thông qua lệnh gọi tới CQueryScan::StartupQuery
. Nhắc nhở về kế hoạch (bấm để phóng to):
Đây là điểm đầu tiên trong quá trình cho đến nay kế hoạch thực hiện trên chuyến bay khả dụng (SQL Server 2016 SP1 trở đi) trong sys.dm_exec_query_statistics_xml
. Không có gì đặc biệt thú vị để xem trong một kế hoạch như vậy vào thời điểm này, bởi vì tất cả các bộ đếm tạm thời đều bằng 0, nhưng kế hoạch ít nhất là có sẵn . Không có dấu hiệu nào cho thấy các nhiệm vụ song song vẫn chưa được tạo hoặc các sàn giao dịch thiếu bên sản xuất. Kế hoạch trông "bình thường" ở mọi khía cạnh.
Các chi nhánh trong kế hoạch song song
Vì đây là một kế hoạch song song, sẽ rất hữu ích nếu bạn chia nhỏ nó thành các nhánh. Chúng được tô bóng bên dưới và được gắn nhãn là các nhánh từ A đến D:
Nhánh A được liên kết với tác vụ mẹ, chạy trên chuỗi công nhân được cung cấp bởi phiên. Các nhân viên song song bổ sung sẽ được bắt đầu chạy các tác vụ song song bổ sung được chứa trong các nhánh B, C và D. Các nhánh này song song với nhau, do đó, DOP sẽ có thêm các nhiệm vụ và nhân công trong mỗi nhánh.
Truy vấn ví dụ của chúng tôi đang chạy ở DOP 2, vì vậy nhánh B sẽ nhận thêm hai tác vụ. Tương tự đối với nhánh C và nhánh D, cho tổng số sáu Nhiệm vụ bổ sung. Mỗi tác vụ sẽ chạy trên chuỗi worker của riêng nó trong ngữ cảnh thực thi của riêng nó.
Hai bộ lên lịch (S 1 và S 2 ) được gán cho truy vấn này để chạy các nhân viên song song bổ sung. Mỗi nhân viên bổ sung sẽ chạy trên một trong hai bộ lập lịch đó. Nhân viên chính có thể chạy trên một bộ lập lịch khác, vì vậy truy vấn DOP 2 của chúng tôi có thể sử dụng tối đa ba các lõi của bộ xử lý tại bất kỳ thời điểm nào.
Tóm lại, kế hoạch của chúng tôi cuối cùng sẽ có:
- Chi nhánh A (phụ huynh)
- Nhiệm vụ dành cho cha mẹ.
- Chuỗi nhân viên chính.
- Ngữ cảnh thực thi bằng không.
- Bất kỳ công cụ lập lịch nào có sẵn cho truy vấn.
- Chi nhánh B (bổ sung)
- Hai nhiệm vụ bổ sung.
- Một chuỗi nhân viên bổ sung liên kết với mỗi nhiệm vụ mới.
- Hai bối cảnh thực thi mới, một ngữ cảnh cho mỗi nhiệm vụ mới.
- Một chuỗi công nhân chạy trên trình lập lịch S 1 . Cái kia chạy trên bộ lập lịch S 2 .
- Chi nhánh C (bổ sung)
- Hai nhiệm vụ bổ sung.
- Một chuỗi nhân viên bổ sung liên kết với mỗi nhiệm vụ mới.
- Hai bối cảnh thực thi mới, một ngữ cảnh cho mỗi nhiệm vụ mới.
- Một chuỗi công nhân chạy trên trình lập lịch S 1 . Cái kia chạy trên bộ lập lịch S 2 .
- Chi nhánh D (bổ sung)
- Hai nhiệm vụ bổ sung.
- Một chuỗi nhân viên bổ sung liên kết với mỗi nhiệm vụ mới.
- Hai bối cảnh thực thi mới, một ngữ cảnh cho mỗi nhiệm vụ mới.
- Một chuỗi công nhân chạy trên trình lập lịch S 1 . Cái kia chạy trên bộ lập lịch S 2 .
Câu hỏi đặt ra là tất cả các nhiệm vụ bổ sung, công nhân và bối cảnh thực thi này được tạo ra như thế nào và khi nào chúng bắt đầu chạy.
Trình tự bắt đầu
Trình tự trong đó nhiệm vụ bổ sung bắt đầu thực hiện cho kế hoạch cụ thể này là:
- Nhánh A (nhiệm vụ chính).
- Nhánh C (các tác vụ song song bổ sung).
- Nhánh D (các tác vụ song song bổ sung).
- Nhánh B (các tác vụ song song bổ sung).
Đó có thể không phải là thứ tự khởi động mà bạn mong đợi.
Có thể có sự chậm trễ đáng kể giữa mỗi bước này, vì lý do mà chúng ta sẽ khám phá ngay sau đây. Điểm mấu chốt ở giai đoạn này là các nhiệm vụ bổ sung, công nhân và bối cảnh thực thi không tất cả được tạo cùng một lúc và chúng không tất cả bắt đầu thực thi cùng một lúc.
SQL Server có thể được thiết kế để khởi động tất cả các bit song song bổ sung cùng một lúc. Điều đó có thể dễ hiểu, nhưng nói chung sẽ không hiệu quả lắm. Nó sẽ tối đa hóa số luồng bổ sung và các tài nguyên khác được sử dụng bởi truy vấn và dẫn đến rất nhiều lần chờ song song không cần thiết.
Với thiết kế được sử dụng bởi SQL Server, các kế hoạch song song thường sẽ sử dụng tổng số luồng công nhân ít hơn (DOP nhân với tổng số nhánh). Điều này đạt được bằng cách nhận ra rằng một số nhánh có thể chạy đến hoàn thành trước khi một số nhánh khác cần bắt đầu. Điều này có thể cho phép sử dụng lại các chuỗi trong cùng một truy vấn và nói chung làm giảm mức tiêu thụ tài nguyên về tổng thể.
Bây giờ chúng ta hãy chuyển sang các chi tiết về cách bắt đầu kế hoạch song song của chúng ta.
Khai trương Chi nhánh A
Quét truy vấn bắt đầu thực thi với tác vụ mẹ gọi Open()
trên trình lặp ở gốc của cây. Đây là phần bắt đầu của trình tự thực thi:
- Nhánh A (nhiệm vụ chính).
- Nhánh C (các tác vụ song song bổ sung).
- Nhánh D (các tác vụ song song bổ sung).
- Nhánh B (các tác vụ song song bổ sung).
Chúng tôi đang thực hiện truy vấn này với một kế hoạch 'thực tế' được yêu cầu, vì vậy trình lặp gốc không toán tử dự án trình tự tại nút 0. Đúng hơn, nó là trình lặp hồ sơ vô hình ghi lại số liệu thời gian chạy trong các kế hoạch chế độ hàng.
Hình minh họa bên dưới cho thấy các trình vòng lặp quét truy vấn trong Nhánh A của kế hoạch, với vị trí của các trình vòng lặp lập hồ sơ vô hình được biểu thị bằng các biểu tượng "kính đeo".
Việc thực thi bắt đầu bằng một lệnh gọi để mở trình cấu hình đầu tiên, CQScanProfileNew::Open
. Điều này đặt thời gian mở cho nhà điều hành dự án trình tự con thông qua API bộ đếm hiệu suất truy vấn của hệ điều hành.
Chúng ta có thể thấy con số này trong sys.dm_exec_query_profiles
:
Các mục nhập ở đó có thể có tên toán tử được liệt kê, nhưng dữ liệu đến từ trình mô tả phía trên toán tử, không phải chính toán tử.
Khi nó xảy ra, một dự án trình tự (CQScanSeqProjectNew
) không cần thực hiện bất kỳ công việc nào khi đã mở , vì vậy nó thực sự không có Open()
phương pháp. Hồ sơ phía trên dự án trình tự là được gọi, vì vậy thời gian mở cho dự án trình tự được ghi lại trong DMV.
Open
của hồ sơ phương thức không gọi Open
trên dự án trình tự (vì nó không có). Thay vào đó, nó gọi Open
trên hồ sơ cho trình lặp tiếp theo theo trình tự. Đây là phân đoạn trình lặp tại nút 1. Điều đó đặt thời gian mở cho phân đoạn, giống như trình biên dịch trước đó đã làm cho dự án trình tự:
Trình lặp phân đoạn hiện có những việc cần làm khi mở, vì vậy lệnh gọi tiếp theo là CQScanSegmentNew::Open
. Khi phân đoạn đã thực hiện những gì cần thiết, nó sẽ gọi trình mô tả cho trình lặp tiếp theo theo trình tự - người tiêu dùng bên của tập hợp các luồng trao đổi tại nút 2:
Lệnh gọi tiếp theo xuống cây quét truy vấn trong quá trình mở là CQScanExchangeNew::Open
, đó là nơi mọi thứ bắt đầu trở nên thú vị hơn.
Mở cuộc trao đổi luồng thu thập
Yêu cầu phía người tiêu dùng của sàn giao dịch mở:
- Mở giao dịch cục bộ (lồng nhau song song) (
CXTransLocal::Open
). Mọi quy trình đều cần một giao dịch có chứa và các tác vụ song song bổ sung cũng không ngoại lệ. Họ không thể chia sẻ trực tiếp giao dịch gốc (cơ sở), vì vậy các giao dịch lồng nhau được sử dụng. Khi một tác vụ song song cần truy cập vào giao dịch cơ sở, nó sẽ đồng bộ hóa trên một chốt và có thể gặp phảiNESTING_TRANSACTION_READONLY
hoặcNESTING_TRANSACTION_FULL
chờ đợi. - Đăng ký luồng công nhân hiện tại với cổng trao đổi (
CXPort::Register
). - Đồng bộ hóa với các chuỗi khác ở phía người tiêu dùng của sàn giao dịch (
sqlmin!CXTransLocal::Synchronize
). Không có chủ đề nào khác ở phía người tiêu dùng của luồng thu thập, vì vậy, đây về cơ bản là không chọn trong dịp này.
Xử lý “Giai đoạn sớm”
Nhiệm vụ chính hiện đã đạt đến cạnh của Nhánh A. Bước tiếp theo là cụ thể sang kế hoạch song song chế độ hàng:Tác vụ mẹ tiếp tục thực thi bằng cách gọi CQScanExchangeNew::EarlyPhases
trên trình vòng lặp trao đổi luồng thu thập tại nút 2. Đây là một phương thức trình vòng lặp bổ sung ngoài Open
thông thường , GetRow
và Close
phương pháp mà nhiều bạn sẽ quen thuộc. EarlyPhases
chỉ được gọi trong các kế hoạch song song ở chế độ hàng.
Tôi muốn nói rõ về điều gì đó vào thời điểm này:Phía nhà sản xuất của luồng thu thập trao đổi tại nút 2 không được tạo chưa và không các nhiệm vụ song song bổ sung đã được tạo. Chúng tôi vẫn đang thực thi mã cho tác vụ mẹ, sử dụng luồng duy nhất đang chạy ngay bây giờ.
Không phải tất cả các trình lặp đều triển khai EarlyPhases
, bởi vì không phải tất cả chúng đều có bất cứ điều gì đặc biệt để làm tại thời điểm này trong kế hoạch song song chế độ hàng. Điều này tương tự với dự án trình tự không triển khai Open
bởi vì nó không có gì để làm tại thời điểm đó. Các trình vòng lặp chính với EarlyPhases
phương pháp là:
-
CQScanConcatNew
(nối). -
CQScanMergeJoinNew
(kết hợp tham gia). -
CQScanSwitchNew
(chuyển đổi). -
CQScanExchangeNew
(song song). -
CQScanNew
(quyền truy cập vào bộ hàng, ví dụ như quét và tìm kiếm). -
CQScanProfileNew
(người lập hồ sơ ẩn). -
CQScanLightProfileNew
(công cụ cấu hình nhẹ vô hình).
Giai đoạn đầu của nhánh B
Nhiệm vụ chính tiếp tục bằng cách gọi EarlyPhases
trên các toán tử con bên ngoài các luồng thu thập trao đổi tại nút 2. Một nhiệm vụ di chuyển qua ranh giới nhánh có thể có vẻ không bình thường, nhưng hãy nhớ rằng ngữ cảnh thực thi số 0 chứa toàn bộ kế hoạch nối tiếp, với các trao đổi bao gồm. Xử lý giai đoạn đầu là về việc khởi chạy chế độ song song, vì vậy nó không được tính dưới dạng thực thi per se .
Để giúp bạn theo dõi, hình ảnh dưới đây cho thấy các trình vòng lặp trong Nhánh B của kế hoạch:
Hãy nhớ rằng, chúng ta vẫn đang ở trong bối cảnh thực thi bằng 0, vì vậy tôi chỉ gọi đây là Chi nhánh B để thuận tiện. Chúng tôi chưa bắt đầu bất kỳ thực thi song song nào.
Trình tự các lệnh gọi mã pha sớm trong Nhánh B là:
-
CQScanProfileNew::EarlyPhases
cho trình biên dịch ở trên nút 3. -
CQScanMergeJoinNew::EarlyPhases
tại nút 3 kết hợp tham gia . -
CQScanProfileNew::EarlyPhases
cho trình mô tả ở trên nút 4. Nút 4 tổng hợp luồng bản thân nó không có phương pháp giai đoạn đầu. -
CQScanProfileNew::EarlyPhases
trên hồ sơ ở trên nút 5. -
CQScanExchangeNew::EarlyPhases
cho luồng phân vùng lại trao đổi tại nút 5.
Lưu ý rằng chúng tôi chỉ xử lý đầu vào bên ngoài (phía trên) cho phép kết hợp ở giai đoạn này. Đây chỉ là trình tự lặp lại thực thi chế độ hàng bình thường. Nó không dành riêng cho các kế hoạch song song.
Giai đoạn đầu của nhánh C
Xử lý giai đoạn đầu tiếp tục với các trình vòng lặp trong Nhánh C:
Trình tự các lệnh gọi ở đây là:
-
CQScanProfileNew::EarlyPhases
cho trình biên dịch ở trên nút 6. -
CQScanProfileNew::EarlyPhases
cho trình biên dịch trên nút 7. -
CQScanProfileNew::EarlyPhases
trên hồ sơ ở trên nút 9. -
CQScanNew::EarlyPhases
cho tìm kiếm chỉ mục tại nút 9.
Không có EarlyPhases
trên luồng tổng hợp hoặc sắp xếp. Công việc do máy tính vô hướng thực hiện tại nút 8 bị hoãn lại (theo kiểu sắp xếp), vì vậy nó không xuất hiện trong cây quét truy vấn và không có trình biên dịch được liên kết.
Giới thiệu về thời gian của tiểu sử
Tác vụ chính xử lý giai đoạn đầu bắt đầu từ việc trao đổi luồng thu thập tại nút 2. Nó đi xuống cây quét truy vấn, theo đầu vào bên ngoài (phía trên) đến phép kết hợp, tất cả các cách xuống tìm kiếm chỉ mục tại nút 9. Trên đường đi, tác vụ mẹ đã gọi EarlyPhases
trên mọi trình lặp hỗ trợ nó.
Cho đến nay, không có hoạt động nào trong các giai đoạn đầu được cập nhật bất kỳ lúc nào trong DMV cấu hình. Cụ thể, không có trình lặp nào bị chạm vào bởi xử lý giai đoạn đầu đã đặt 'thời gian mở'. Điều này có ý nghĩa, bởi vì xử lý giai đoạn đầu chỉ là thiết lập thực thi song song - các toán tử này sẽ được mở để thực thi sau này.
Tìm kiếm chỉ mục tại nút 9 là một nút lá - nó không có con. Tác vụ mẹ bây giờ bắt đầu quay trở lại từ EarlyPhases
lồng nhau cuộc gọi, tăng dần cây quét truy vấn trở lại trao đổi luồng thu thập.
Mỗi bộ cấu hình gọi Bộ đếm hiệu suất truy vấn API khi nhập vào EarlyPhases
của họ và họ sẽ gọi lại phương pháp này khi đang xuất hiện. Sự khác biệt giữa hai số thể hiện thời gian đã trôi qua cho trình lặp và tất cả các trình lặp con của nó (vì các cuộc gọi phương thức được lồng vào nhau).
Sau khi trình cấu hình cho tìm kiếm chỉ mục trả về, DMV cấu hình hiển thị thời gian CPU đã trôi qua và cho tìm kiếm chỉ mục chỉ, cũng như một hoạt động gần đây nhất được cập nhật thời gian. Cũng xin lưu ý rằng thông tin này được ghi lại đối với tác vụ mẹ (lựa chọn duy nhất hiện tại):
Không có trình lặp trước nào được các cuộc gọi giai đoạn đầu chạm vào có thời gian trôi qua hoặc cập nhật thời gian hoạt động cuối cùng. Những con số này chỉ được cập nhật khi chúng tôi lên cây.
Sau khi cuộc gọi trả về giai đoạn đầu của trình mô tả tiếp theo, sắp xếp thời gian được cập nhật:
Lần trở lại tiếp theo đưa chúng ta qua trình mô tả cho tổng hợp luồng tại nút 6:
Quay trở lại từ trình biên dịch này đưa chúng ta trở lại EarlyPhases
gọi theo luồng phân vùng lại trao đổi tại nút 5 . Hãy nhớ rằng đây không phải là nơi bắt đầu chuỗi các lệnh gọi giai đoạn đầu - đó là các luồng thu thập trao đổi tại nút 2.
Nhiệm vụ Song song Nhánh C Đã được Yêu cầu
Ngoài việc cập nhật dữ liệu hồ sơ, các cuộc gọi giai đoạn đầu trước đó dường như không thực hiện được nhiều. Tất cả đều thay đổi với luồng phân vùng lại trao đổi tại nút 5.
Tôi sẽ mô tả Chi nhánh C một cách chi tiết để giới thiệu một số khái niệm quan trọng, những khái niệm này cũng sẽ áp dụng cho các nhánh song song khác. Việc đề cập đến vấn đề này ngay bây giờ có nghĩa là cuộc thảo luận nhánh sau này có thể ngắn gọn hơn.
Sau khi hoàn thành xử lý giai đoạn đầu lồng nhau cho cây con của nó (xuống đến tìm kiếm chỉ mục tại nút 9), sàn giao dịch có thể bắt đầu công việc giai đoạn đầu của riêng nó. Điều này bắt đầu giống như mở đầu các luồng thu thập trao đổi tại nút 2:
-
CXTransLocal::Open
(mở giao dịch phụ song song cục bộ). -
CXPort::Register
(đăng ký với cổng trao đổi).
Các bước tiếp theo khác nhau vì nhánh C chứa đầy đủ chặn trình lặp (sắp xếp ở nút 7). Quá trình xử lý giai đoạn đầu tại các luồng phân vùng lại nút 5 thực hiện như sau:
- Cuộc gọi
CQScanExchangeNew::StartAllProducers
. Đây là lần đầu tiên chúng tôi gặp phải bất kỳ điều gì liên quan đến phía nhà sản xuất của sàn giao dịch. Nút 5 là cuộc trao đổi đầu tiên trong kế hoạch này nhằm tạo ra phía nhà sản xuất. - Có được mutex vì vậy không có chuỗi nào khác có thể xếp hàng đợi các tác vụ cùng một lúc.
- Bắt đầu các giao dịch lồng nhau song song cho các tác vụ của nhà sản xuất (
CXPort::StartNestedTransactions
vàReadOnlyXactImp::BeginParallelNestedXact
). - Đăng ký các giao dịch con với đối tượng quét truy vấn chính (
CQueryScan::AddSubXact
). - Tạo bộ mô tả trình sản xuất (
CQScanExchangeNew::PxproddescCreate
). - Tạo bối cảnh thực thi của nhà sản xuất mới (
CExecContext
) bắt nguồn từ ngữ cảnh thực thi bằng không. - Cập nhật bản đồ được liên kết của các trình lặp kế hoạch.
- Đặt DOP cho ngữ cảnh mới (
CQueryExecContext::SetDop
) để tất cả các nhiệm vụ đều biết cài đặt DOP tổng thể là gì. - Khởi tạo bộ đệm tham số (
CQueryExecContext::InitParamCache
). - Liên kết các giao dịch lồng nhau song song với giao dịch cơ sở (
CExecContext::SetBaseXact
). - Xếp hàng đợi các quy trình con mới để thực thi (
SubprocessMgr::EnqueueMultipleSubprocesses
). - Tạo nhiệm vụ song song mới nhiệm vụ thông qua
sqldk!SOS_Node::EnqueueMultipleTasksDirect
.
Ngăn xếp cuộc gọi của nhiệm vụ chính (dành cho những người trong số bạn thích những điều này) vào thời điểm này là:
Kết thúc Phần Ba
Hiện chúng tôi đã tạo phía nhà sản xuất của các luồng phân vùng lại trao đổi tại nút 5, đã tạo thêm các tác vụ song song để chạy Chi nhánh C và liên kết mọi thứ trở lại cha cấu trúc theo yêu cầu. Chi nhánh C là đầu tiên nhánh để bắt đầu bất kỳ nhiệm vụ song song nào. Phần cuối của loạt bài này sẽ xem xét chi tiết việc mở nhánh C và bắt đầu các nhiệm vụ song song còn lại.