Database
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Database

Cách các kế hoạch song song bắt đầu - Phần 2

Đây là phần thứ hai của loạt bài gồm năm phần đi sâu vào cách khởi động các kế hoạch song song của chế độ hàng SQL Server. Vào cuối phần đầu tiên, chúng tôi đã tạo ngữ cảnh thực thi bằng không cho nhiệm vụ cha. Ngữ cảnh này chứa toàn bộ cây các toán tử thực thi, nhưng chúng chưa sẵn sàng cho mô hình thực thi lặp đi lặp lại của công cụ xử lý truy vấn.

Thực thi Lặp lại

SQL Server thực thi một truy vấn thông qua một quá trình được gọi là quét truy vấn . Khởi tạo kế hoạch bắt đầu từ gốc bởi bộ xử lý truy vấn gọi Open trên nút gốc. Open các lệnh gọi đi ngang qua cây các trình vòng lặp gọi một cách đệ quy Open trên mỗi con cho đến khi toàn bộ cây được mở ra.

Quá trình trả về các hàng kết quả cũng là đệ quy, được kích hoạt bởi bộ xử lý truy vấn gọi GetRow Tại gốc rễ. Mỗi lệnh gọi gốc trả về một hàng tại một thời điểm. Bộ xử lý truy vấn tiếp tục gọi GetRow trên nút gốc cho đến khi không còn hàng nào nữa. Quá trình thực thi bị tắt với một Close đệ quy cuối cùng gọi điện. Sự sắp xếp này cho phép bộ xử lý truy vấn khởi tạo, thực thi và đóng bất kỳ kế hoạch tùy ý nào bằng cách gọi các phương thức giao diện giống nhau ngay tại thư mục gốc.

Để chuyển đổi cây các toán tử thực thi thành một toán tử phù hợp để xử lý từng hàng, SQL Server thêm quét truy vấn trình bao bọc cho mỗi nhà điều hành. Quét truy vấn đối tượng cung cấp Open , GetRowClose các phương thức cần thiết để thực thi lặp đi lặp lại.

Đối tượng quét truy vấn cũng duy trì thông tin trạng thái và hiển thị các phương thức dành riêng cho toán tử khác cần thiết trong quá trình thực thi. Ví dụ:đối tượng quét truy vấn cho toán tử Bộ lọc Khởi động (CQScanStartupFilterNew ) cho thấy các phương pháp sau:

  • Open
  • GetRow
  • Close
  • PrepRecompute
  • GetScrollLock
  • SetMarker
  • GotoMarker
  • GotoLocation
  • ReverseDirection
  • Dormant

Các phương thức bổ sung cho trình lặp này hầu hết được sử dụng trong các kế hoạch con trỏ.

Khởi chạy Quét Truy vấn

Quá trình gói được gọi là khởi tạo quét truy vấn . Nó được thực hiện bởi một cuộc gọi từ bộ xử lý truy vấn tới CQueryScan::InitQScanRoot . Nhiệm vụ chính thực hiện quá trình này cho toàn bộ kế hoạch (được chứa trong ngữ cảnh thực thi bằng không). Bản thân quá trình dịch là đệ quy, bắt đầu từ gốc và hoạt động theo cách của nó xuống cây.

Trong quá trình này, mỗi nhà điều hành chịu trách nhiệm khởi tạo dữ liệu của riêng mình và tạo mọi tài nguyên thời gian chạy nó cần. Điều này có thể bao gồm việc tạo các đối tượng bổ sung bên ngoài bộ xử lý truy vấn, chẳng hạn như các cấu trúc cần thiết để giao tiếp với công cụ lưu trữ để tìm nạp dữ liệu từ bộ nhớ liên tục.

Nhắc nhở về kế hoạch thực hiện, có thêm số nút (bấm để phóng to):

Toán tử ở root (nút 0) của cây kế hoạch thực thi là một dự án trình tự . Nó được đại diện bởi một lớp có tên CXteSeqProject . Như thường lệ, đây là nơi bắt đầu chuyển đổi đệ quy.

Trình bao bọc quét truy vấn

Như đã đề cập, CXteSeqProject đối tượng không được trang bị để tham gia vào quá trình quét truy vấn lặp đi lặp lại quy trình - nó không có Open bắt buộc , GetRowClose các phương pháp. Bộ xử lý truy vấn cần một trình bao bọc xung quanh toán tử thực thi để cung cấp giao diện đó.

Để có được trình bao bọc quét truy vấn đó, tác vụ mẹ gọi CXteSeqProject::QScanGet để trả về một đối tượng kiểu CQScanSeqProjectNew . Bản đồ được liên kết trong số các toán tử được tạo trước đó được cập nhật để tham chiếu đối tượng quét truy vấn mới và các phương thức trình lặp của nó được kết nối với gốc của kế hoạch.

Con của dự án trình tự là một phân đoạn toán tử (nút 1). Gọi CXteSegment::QScanGet trả về đối tượng trình bao bọc quét truy vấn thuộc loại CQScanSegmentNew . Bản đồ được liên kết lại được cập nhật và các con trỏ hàm của trình vòng lặp được kết nối với quá trình quét truy vấn dự án trình tự mẹ.

Một nửa trao đổi

Toán tử tiếp theo là luồng thu thập trao đổi (nút 2). Gọi CXteExchange::QScanGet trả về một CQScanExchangeNew như bạn có thể mong đợi bây giờ.

Đây là toán tử đầu tiên trong cây cần thực hiện khởi tạo bổ sung đáng kể. Nó tạo ra phía người tiêu dùng trao đổi qua CXTransport::CreateConsumerPart . Điều này tạo ra cổng (CXPort ) - cấu trúc dữ liệu trong bộ nhớ dùng chung được sử dụng để đồng bộ hóa và trao đổi dữ liệu - và một đường ống (CXPipe ) để vận chuyển gói. Lưu ý rằng nhà sản xuất phía của sàn giao dịch không được tạo tại thời điểm này. Chúng tôi chỉ có một nửa trao đổi!

Nhiều gói hơn

Sau đó, quá trình thiết lập quét bộ xử lý truy vấn tiếp tục với kết hợp hợp nhất (nút 3). Không phải lúc nào tôi cũng lặp lại QScanGetCQScan* từ thời điểm này trở đi, nhưng chúng tuân theo mô hình đã thiết lập.

Phép hợp nhất có hai phần tử con. Quá trình thiết lập quét truy vấn tiếp tục như trước với đầu vào bên ngoài (trên cùng) - tổng hợp luồng (nút 4), sau đó phân vùng lại luồng trao đổi (nút 5). Các luồng phân vùng lại một lần nữa chỉ tạo ra phía người tiêu dùng của sàn giao dịch, nhưng lần này có hai đường ống được tạo vì DOP là hai. Phía người tiêu dùng của loại trao đổi này có các kết nối DOP với nhà điều hành chính của nó (một trên mỗi luồng).

Tiếp theo, chúng tôi có một tổng hợp luồng khác (nút 6) và sắp xếp (nút 7). Sắp xếp có phần tử con không hiển thị trong các kế hoạch thực thi - một bộ hàng của công cụ lưu trữ được sử dụng để triển khai tràn tới tempdb . CQScanSortNew dự kiến do đó đi kèm với một CQScanRowsetNew con trong cây nội bộ. Nó không hiển thị trong đầu ra chương trình.

Lập hồ sơ I / O và hoạt động hoãn lại

Sắp xếp cũng là nhà điều hành đầu tiên chúng tôi khởi tạo cho đến nay có thể chịu trách nhiệm về I / O . Giả sử việc thực thi đã yêu cầu dữ liệu hồ sơ I / O (ví dụ:bằng cách yêu cầu kế hoạch 'thực tế'), sắp xếp sẽ tạo một đối tượng để ghi lại thời gian chạy này dữ liệu hồ sơ qua CProfileInfo::AllocProfileIO .

Toán tử tiếp theo là tính toán vô hướng (nút 8), được gọi là dự án trong nội bộ. Lệnh gọi thiết lập quét truy vấn tới CXteProject::QScanGet không trả về một đối tượng quét truy vấn, bởi vì các phép tính được thực hiện bởi đại lượng tính toán này được hoãn lại tới toán tử cha đầu tiên cần kết quả. Trong kế hoạch này, toán tử đó là loại. Sắp xếp sẽ thực hiện tất cả công việc được giao cho tính toán vô hướng, vì vậy dự án tại nút 8 không tạo thành một phần của cây quét truy vấn. Tính vô hướng thực sự không được thực thi trong thời gian chạy. Để biết thêm chi tiết về các tỷ lệ tính toán hoãn lại, hãy xem Tính toán tỷ lệ, Biểu thức và Hiệu suất Kế hoạch Thực thi.

Quét song song

Toán tử cuối cùng sau đại lượng vô hướng máy tính trên nhánh này của kế hoạch là tìm kiếm chỉ mục (CXteRange ) tại nút 9. Điều này tạo ra toán tử quét truy vấn dự kiến ​​(CQScanRangeNew ), nhưng nó cũng yêu cầu một chuỗi khởi tạo phức tạp để kết nối với công cụ lưu trữ và tạo điều kiện quét chỉ mục song song.

Chỉ bao gồm các điểm nổi bật, khởi tạo tìm kiếm chỉ mục:

  • Tạo đối tượng cấu hình cho I / O (CProfileInfo::AllocProfileIO ).
  • Tạo bộ hàng song song quét truy vấn (CQScanRowsetNew::ParallelGetRowset ).
  • Thiết lập đồng bộ hóa đối tượng để điều phối quét phạm vi song song trong thời gian chạy (CQScanRangeNew::GetSyncInfo ).
  • Tạo công cụ lưu trữ con trỏ bảng bộ mô tả giao dịch chỉ đọc .
  • Mở bộ hàng mẹ để đọc (truy cập HoBt và lấy các chốt cần thiết).
  • Đặt thời gian chờ khóa.
  • Thiết lập tìm nạp trước (bao gồm cả bộ đệm bộ nhớ được liên kết).

Thêm toán tử lập hồ sơ chế độ hàng

Bây giờ chúng ta đã đạt đến cấp độ lá của nhánh này của kế hoạch (tìm kiếm chỉ mục không có con). Vừa tạo đối tượng quét truy vấn cho tìm kiếm chỉ mục, bước tiếp theo là kết thúc quét truy vấn với một lớp cấu hình (giả sử chúng tôi đã yêu cầu một kế hoạch thực tế). Điều này được thực hiện bằng lệnh gọi tới sqlmin!PqsWrapQScan . Lưu ý rằng các trình cấu hình được thêm vào sau khi tạo xong quá trình quét truy vấn, khi chúng ta bắt đầu đi lên cây trình vòng lặp.

PqsWrapQScan tạo một toán tử hồ sơ mới với tư cách là cha của tìm kiếm chỉ mục, thông qua lệnh gọi đến CProfileInfo::GetOrCreateProfileInfo . Toán tử lập hồ sơ (CQScanProfileNew ) có các phương thức giao diện quét truy vấn thông thường. Ngoài việc thu thập dữ liệu cần thiết cho các kế hoạch thực tế, dữ liệu cấu hình cũng được hiển thị qua DMV sys.dm_exec_query_profiles .

Việc truy vấn DMV đó tại thời điểm chính xác này trong thời gian cho phiên hiện tại cho thấy rằng chỉ tồn tại một toán tử kế hoạch duy nhất (nút 9) (có nghĩa là nó là đơn vị duy nhất được bao bọc bởi một trình biên dịch):

Ảnh chụp màn hình này hiển thị tập hợp kết quả hoàn chỉnh từ DMV tại thời điểm hiện tại (nó chưa được chỉnh sửa).
Tiếp theo, CQScanProfileNew gọi API bộ đếm hiệu suất truy vấn (KERNEL32!QueryPerformanceCounterStub ) do hệ điều hành cung cấp để ghi lại thời gian hoạt động đầu tiên và cuối cùng của toán tử được mô tả:

Thời gian hoạt động gần đây nhất sẽ được cập nhật bằng cách sử dụng API bộ đếm hiệu suất truy vấn mỗi khi mã cho trình vòng lặp đó chạy.
Sau đó, trình cấu hình đặt số hàng ước tính tại thời điểm này trong kế hoạch (CProfileInfo::SetCardExpectedRows ), tính cho bất kỳ mục tiêu hàng nào (CXte::CardGetRowGoal ). Vì đây là một kế hoạch song song, nó chia kết quả cho số luồng (CXte::FGetRowGoalDefinedForOneThread ) và lưu kết quả trong ngữ cảnh thực thi.

Số hàng ước tính không hiển thị thông qua DMV tại thời điểm này, bởi vì tác vụ mẹ sẽ không thực thi toán tử này. Thay vào đó, ước tính mỗi luồng sẽ được hiển thị sau đó trong các ngữ cảnh thực thi song song (chưa được tạo). Tuy nhiên, số trên mỗi chuỗi được lưu trong hồ sơ của nhiệm vụ chính - nó không hiển thị qua DMV.
Tên thân thiện của toán tử kế hoạch (“Tìm kiếm chỉ mục”) sau đó được đặt thông qua lệnh gọi tới CXteRange::GetPhysicalOp :

Trước đó, bạn có thể nhận thấy rằng truy vấn DMV hiển thị tên là “???”. Đây là tên cố định được hiển thị cho các toán tử ẩn (ví dụ:tìm nạp trước các vòng lặp lồng nhau, sắp xếp hàng loạt) không có tên thân thiện được xác định.

Cuối cùng, siêu dữ liệu chỉ mục và thống kê I / O hiện tại đối với tìm kiếm chỉ mục được bao bọc được thêm thông qua lệnh gọi đến CQScanRowsetNew::GetIoCounters :

Các bộ đếm bằng 0 tại thời điểm này, nhưng sẽ được cập nhật khi tìm kiếm chỉ mục thực hiện I / O trong quá trình thực thi kế hoạch hoàn tất.

Xử lý quét truy vấn khác

Với toán tử cấu hình được tạo cho tìm kiếm chỉ mục, xử lý quét truy vấn sẽ di chuyển ngược cây về sắp xếp chính (nút 7).

Sắp xếp thực hiện các tác vụ khởi tạo sau:

  • Đăng ký mức sử dụng bộ nhớ của nó với truy vấn trình quản lý bộ nhớ (CQryMemManager::RegisterMemUsage )
  • Tính toán bộ nhớ cần thiết cho đầu vào sắp xếp (CQScanIndexSortNew::CbufInputMemory ) và đầu ra (CQScanSortNew::CbufOutputMemory ).
  • Bảng sắp xếp được tạo, cùng với bộ công cụ lưu trữ được liên kết với nó (sqlmin!RowsetSorted ).
  • Một giao dịch hệ thống độc lập (không bị ràng buộc bởi giao dịch người dùng) được tạo để sắp xếp phân bổ đĩa tràn, cùng với một bảng làm việc giả mạo (sqlmin!CreateFakeWorkTable ).
  • Dịch vụ biểu thức được khởi tạo (sqlTsEs!CEsRuntime::Startup ) để toán tử sắp xếp thực hiện các phép tính hoãn lại từ tính toán vô hướng.
  • Tìm nạp trước đối với bất kỳ loại nào chạy tràn đến tempdb sau đó được tạo qua (CPrefetchMgr::SetupPrefetch ).

Cuối cùng, quá trình quét truy vấn sắp xếp được bao bọc bởi một toán tử cấu hình (bao gồm cả I / O) giống như chúng ta đã thấy đối với tìm kiếm chỉ mục:

Lưu ý rằng tính vô hướng (nút 8) bị thiếu từ DMV. Đó là bởi vì công việc của nó được chuyển sang sắp xếp, không phải là một phần của cây quét truy vấn và do đó không có đối tượng trình biên dịch gói.

Chuyển lên cấp độ gốc của loại, tổng hợp luồng toán tử quét truy vấn (nút 6) khởi tạo các biểu thức và bộ đếm thời gian chạy của nó (ví dụ:số hàng nhóm hiện tại). Tổng hợp luồng được bao bọc bằng toán tử cấu hình, ghi lại thời gian ban đầu của nó:

Phân vùng gốc luồng trao đổi (nút 5) được bao bọc bởi một trình biên dịch (hãy nhớ rằng chỉ có phía người tiêu dùng của sàn giao dịch này tồn tại vào thời điểm này):

Điều tương tự cũng được thực hiện đối với tổng hợp luồng chính của nó (nút 4), cũng được khởi tạo như đã mô tả trước đây:

Quá trình quét truy vấn trả về liên kết hợp nhất chính (nút 3) nhưng chưa khởi tạo nó. Thay vào đó, chúng tôi di chuyển xuống phía bên trong (phía dưới) của phép nối hợp nhất, thực hiện các tác vụ chi tiết tương tự cho các toán tử đó (các nút 10 đến 15) như được thực hiện cho nhánh trên (bên ngoài):

Sau khi các toán tử đó được xử lý, liên kết hợp nhất quét truy vấn được tạo, khởi tạo và bao bọc bằng một đối tượng cấu hình. Điều này bao gồm các bộ đếm I / O vì liên kết hợp nhất nhiều người sử dụng một bảng làm việc (mặc dù liên kết hợp nhất hiện tại là một-nhiều):

Quy trình tương tự cũng được thực hiện đối với luồng thu thập chính Exchange (nút 2) chỉ phía người tiêu dùng, phân khúc (nút 1) và dự án trình tự (nút 0) toán tử. Tôi sẽ không mô tả chi tiết về chúng.

Các cấu hình truy vấn DMV hiện báo cáo một tập hợp đầy đủ các nút quét truy vấn được bao bọc bởi bộ cấu hình:

Lưu ý rằng dự án trình tự, phân đoạn và luồng thu thập mà người tiêu dùng có số hàng ước tính bởi vì các toán tử này sẽ được điều hành bởi tác vụ chính , không phải bởi các tác vụ song song bổ sung (xem CXte::FGetRowGoalDefinedForOneThread sớm hơn). Tác vụ mẹ không có công việc nào để thực hiện trong các nhánh song song, vì vậy khái niệm số hàng ước tính chỉ có ý nghĩa đối với các tác vụ bổ sung.

Các giá trị thời gian hoạt động hiển thị ở trên có phần bị bóp méo vì tôi cần dừng thực thi và chụp ảnh màn hình DMV ở mỗi bước. Một quá trình thực thi riêng biệt (không có độ trễ giả tạo do sử dụng trình gỡ lỗi) tạo ra các thời gian sau:

Cây được cấu tạo theo trình tự giống như mô tả trước đây, nhưng quá trình diễn ra quá nhanh chỉ có 1 micro giây chênh lệch giữa thời gian hoạt động của toán tử được bao bọc đầu tiên (tìm kiếm chỉ mục tại nút 9) và cuối cùng (dự án trình tự tại nút 0).

Kết thúc Phần 2

Nghe có vẻ như chúng tôi đã làm rất nhiều việc, nhưng hãy nhớ rằng chúng tôi chỉ tạo một cây quét truy vấn cho tác vụ mẹ , và các sàn giao dịch chỉ có phía người tiêu dùng (chưa có nhà sản xuất). Kế hoạch song song của chúng tôi cũng chỉ có một chủ đề (như được hiển thị trong ảnh chụp màn hình cuối cùng). Phần 3 sẽ xem việc tạo các nhiệm vụ song song bổ sung đầu tiên của chúng tôi.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Cách đếm số hàng trong bảng trong SQL

  2. Mức cách ly có thể nối tiếp hóa

  3. Chờ thống kê và kho truy vấn

  4. Giảm thiểu tác động của việc mở rộng cột IDENTITY - phần 4

  5. Toán tử SQL Equals (=) cho người mới bắt đầu