LOB_DATA, quét bảng chậm và một số câu hỏi I / O


19

Tôi có một bảng khá lớn với một trong các cột là dữ liệu XML với kích thước trung bình của mục nhập XML là ~ 15 kilobyte. Tất cả các cột khác là ints thông thường, bigint, GUID, v.v. Để có một số con số cụ thể, giả sử bảng có một triệu hàng và có kích thước ~ 15 GB.

Điều tôi nhận thấy là bảng này rất chậm để chọn dữ liệu nếu tôi muốn chọn tất cả các cột. Khi tôi làm

SELECT TOP 1000 * FROM TABLE

mất khoảng 20-25 giây để đọc dữ liệu từ đĩa - mặc dù tôi không áp đặt bất kỳ yêu cầu nào đối với kết quả. Tôi chạy truy vấn với bộ đệm lạnh (tức là sau DBCC DROPCLEANBUFFERS). Đây là kết quả thống kê IO:

Quét số 1, đọc logic 364, đọc vật lý 24, đọc trước 7191, đọc logic lob 7924, đọc vật lý lob 1690, đọc trước đọc trước 3968.

Nó lấy ~ 15 MB dữ liệu. Kế hoạch thực hiện cho thấy Quét chỉ mục cụm như tôi mong đợi.

Không có IO nào đang diễn ra trên đĩa ngoài các truy vấn của tôi; Tôi cũng đã kiểm tra phân mảnh chỉ mục được phân cụm gần 0%. Đây là ổ đĩa SATA cấp độ người tiêu dùng, tuy nhiên tôi vẫn nghĩ rằng SQL Server có thể quét bảng nhanh hơn ~ 100-150 MB / phút.

Sự hiện diện của trường XML khiến hầu hết dữ liệu bảng được đặt trên các trang LOB_DATA (thực tế ~ 90% các trang của bảng là LOB_DATA).

Tôi đoán câu hỏi của tôi là - tôi có đúng không khi nghĩ rằng các trang LOB_DATA có thể gây ra các bản quét chậm không chỉ vì kích thước của chúng, mà còn bởi vì SQL Server không thể quét chỉ mục được nhóm một cách hiệu quả khi có nhiều trang LOB_DATA trong bảng?

Thậm chí rộng hơn - nó có được coi là hợp lý khi có cấu trúc bảng / mẫu dữ liệu như vậy không? Các đề xuất cho việc sử dụng Filestream thường nêu kích thước trường lớn hơn nhiều, vì vậy tôi không thực sự muốn đi theo con đường đó. Tôi đã không thực sự tìm thấy bất kỳ thông tin tốt về kịch bản cụ thể này.

Tôi đã suy nghĩ về việc nén XML, nhưng nó cần được thực hiện trên máy khách hoặc với SQLCLR và sẽ yêu cầu khá nhiều công việc để thực hiện trong hệ thống.

Tôi đã thử nén và vì các XML rất dư thừa, tôi có thể (trong ứng dụng ac #) nén XML từ 20KB xuống ~ 2,5KB và lưu trữ nó trong cột VARBINARY, ngăn việc sử dụng các trang dữ liệu LOB. Tốc độ này CHỌN 20 lần trong các thử nghiệm của tôi.


Alex: không chắc chắn nếu bạn thấy cuộc thảo luận liên quan đến câu trả lời của tôi (liên kết nằm trong một bình luận bên dưới câu trả lời của tôi), nhưng tôi đã có thể tiến gần đến việc tái tạo kịch bản của bạn. Tôi đã điền một bảng khớp (nhiều như tôi đã có thông tin) cho mô tả của bạn và có các số liệu thống kê I / O rất giống nhau. Ngoại trừ, "LOB vật lý đọc" thậm chí không bao giờ gần. Vì vậy, tôi đã tự hỏi nếu bạn cập nhật XML (nhưng không phải các cột khác) và / hoặc có nhiều phân mảnh vật lý của các tệp dữ liệu của bạn. Tôi vẫn không phiền khi nhận DDL của bảng và cài đặt tăng trưởng tự động của bạn cho mỗi tệp dữ liệu và bạn có thu nhỏ tệp dữ liệu của mình không?
Solomon Rutzky

Trước hết - cảm ơn rất nhiều vì đã trả lời chi tiết, tôi đã không thể tham gia thảo luận vào thời điểm đó do thiếu thời gian. Bây giờ bạn đã đề cập đến điều này (tôi không nghĩ về nó khi được hỏi câu hỏi) - Trường XML được cập nhật nhiều lần sau khi được tạo và nó được tạo nhỏ. Vì vậy, tôi nghi ngờ rằng ban đầu nó được lưu trữ liên tiếp và sau một số cập nhật, nó sẽ được chuyển sang cấu trúc trang LOB và sau đó nhận thêm một số cập nhật.
Alexander Shelemin

(Tiếp tục) Tôi đã kiểm tra sự phân mảnh vật lý của các tệp trước khi đặt câu hỏi và công cụ Windows tích hợp nghĩ rằng nó ổn, vì vậy tôi không xem xét thêm nữa. Tôi tin rằng tự động tăng trưởng là 1 MB và các tệp dữ liệu không bị thu hẹp.
Alexander Shelemin

Chọn 1000 * hàng đầu có tầm quan trọng trong trường hợp cụ thể của tôi. Tôi chắc chắn hiểu rằng đó được coi là một thực tiễn tồi, tuy nhiên một số quyết định thiết kế ứng dụng thực sự khó thay đổi sau khi chúng được đưa ra trong một thời gian dài. Chọn * về cơ bản được sử dụng làm chiến lược sao chép cơ sở dữ liệu chéo giữa các thành phần khác nhau trong ứng dụng của chúng tôi. Có nhiều ưu điểm, ví dụ chúng ta có thể thực hiện nhiều thao tác tùy ý với dữ liệu / lược đồ, điều này sẽ khó với các kỹ thuật sao chép tích hợp, nhưng nó đi kèm với các vấn đề của nó.
Alexander Shelemin

Alex, SELECT *không phải là vấn đề nếu bạn đang cần dữ liệu XML. Đây chỉ là một vấn đề nếu bạn không muốn dữ liệu XML, trong trường hợp đó tại sao làm chậm truy vấn để lấy lại dữ liệu mà bạn không sử dụng? Tôi đã hỏi về các bản cập nhật cho XML tự hỏi liệu phân mảnh trên các trang LOB không được báo cáo chính xác. Đó là lý do tại sao tôi đã hỏi trong câu trả lời của mình, làm thế nào chính xác bạn đã xác định rằng chỉ số cụm không bị phân mảnh? Bạn có thể cung cấp lệnh bạn đã chạy? Và bạn đã thực hiện một REBUILD đầy đủ trên Chỉ số cụm? (còn tiếp)
Solomon Rutzky

Câu trả lời:


11

Sự hiện diện của trường XML khiến hầu hết dữ liệu bảng được đặt trên các trang LOB_DATA (thực tế ~ 90% các trang của bảng là LOB_DATA).

Chỉ có cột XML trong bảng không có hiệu ứng đó. Đó là sự hiện diện của dữ liệu XML , trong một số điều kiện nhất định , khiến một phần dữ liệu của một hàng được lưu trữ ngoài hàng, trên các trang LOB_DATA. Và trong khi một (hoặc có thể một số ;-) có thể lập luận rằng duh, thì XMLcột ngụ ý rằng thực sự sẽ có dữ liệu XML, không đảm bảo rằng dữ liệu XML sẽ cần được lưu trữ ngoài hàng: trừ khi hàng đã được điền khá nhiều bên ngoài bất kỳ dữ liệu XML nào, các tài liệu nhỏ (tối đa 8000 byte) có thể phù hợp với nhau và không bao giờ truy cập trang LOB_DATA.

Tôi có đúng không khi nghĩ rằng các trang LOB_DATA có thể gây ra các bản quét chậm không chỉ vì kích thước của chúng mà còn bởi vì SQL Server không thể quét chỉ mục được nhóm một cách hiệu quả khi có nhiều trang LOB_DATA trong bảng?

Quét liên quan đến việc nhìn vào tất cả các hàng. Tất nhiên, khi một trang dữ liệu được đọc, tất cả dữ liệu liên tiếp sẽ được đọc, ngay cả khi bạn đã chọn một tập hợp con của các cột. Sự khác biệt với dữ liệu LOB là nếu bạn không chọn cột đó, thì dữ liệu ngoài hàng sẽ không được đọc. Do đó, thật không công bằng khi đưa ra kết luận về việc SQL Server có thể quét Chỉ mục cụm này hiệu quả như thế nào vì bạn đã không kiểm tra chính xác điều đó (hoặc bạn đã kiểm tra một nửa số đó). Bạn đã chọn tất cả các cột, bao gồm cột XML và như bạn đã đề cập, đó là nơi chứa hầu hết dữ liệu.

Vì vậy, chúng tôi đã biết rằng SELECT TOP 1000 *bài kiểm tra không chỉ đơn thuần là đọc một loạt các trang dữ liệu 8k, tất cả trong một hàng, mà thay vào đó nhảy đến các vị trí khác trên mỗi hàng . Cấu trúc chính xác của dữ liệu LOB đó có thể thay đổi dựa trên mức độ lớn của nó. Dựa trên nghiên cứu được hiển thị ở đây ( Kích thước của Con trỏ LOB cho các loại (MAX) như Varchar, Varbinary, Etc? ), Có hai loại phân bổ LOB ngoài hàng:

  1. Root nội tuyến - đối với dữ liệu từ 8001 đến 40.000 (thực sự là 42.000) byte, cho phép không gian, sẽ có 1 đến 5 con trỏ (24 - 72 byte) IN ROW trỏ trực tiếp vào (các) trang LOB.
  2. TEXT_TREE - đối với dữ liệu trên 42.000 byte hoặc nếu 1 đến 5 con trỏ không khớp với nhau, thì sẽ chỉ có một con trỏ 24 byte đến trang bắt đầu của danh sách các con trỏ tới các trang LOB (tức là " text_tree "trang).

Một trong hai tình huống này xảy ra mỗi khi bạn truy xuất dữ liệu LOB lớn hơn 8000 byte hoặc không phù hợp. Tôi đã đăng một tập lệnh thử nghiệm trên PasteBin.com (tập lệnh T-SQL để kiểm tra phân bổ và đọc LOB ) cho thấy 3 loại phân bổ LOB (dựa trên kích thước của dữ liệu) cũng như hiệu ứng của từng loại đó đối với logic và đọc vật lý. Trong trường hợp của bạn, nếu dữ liệu XML thực sự ít hơn 42.000 byte mỗi hàng, thì không có dữ liệu nào trong số đó (hoặc rất ít trong số đó) phải ở trong cấu trúc TEXT_TREE kém hiệu quả nhất.

Nếu bạn muốn kiểm tra SQL Server có thể quét Chỉ mục cụm đó nhanh như thế nào, hãy thực hiện SELECT TOP 1000nhưng chỉ định một hoặc nhiều cột không bao gồm cột XML đó. Điều đó ảnh hưởng đến kết quả của bạn như thế nào? Nó sẽ khá nhanh hơn một chút.

nó có được coi là hợp lý để có cấu trúc bảng / mẫu dữ liệu như vậy không?

Cho rằng chúng ta có một mô tả không đầy đủ về cấu trúc bảng dữ liệu và mẫu dữ liệu thực tế, bất kỳ câu trả lời nào có thể không tối ưu tùy thuộc vào những chi tiết bị thiếu đó là gì. Với ý nghĩ đó, tôi sẽ nói rằng rõ ràng không có gì bất hợp lý về cấu trúc bảng hoặc mẫu dữ liệu của bạn.

Tôi có thể (trong ứng dụng ac #) nén XML từ 20KB xuống ~ 2,5KB và lưu trữ nó trong cột VARBINARY, ngăn chặn việc sử dụng các trang dữ liệu LOB. Tốc độ này CHỌN 20 lần trong các thử nghiệm của tôi.

Điều đó làm cho việc chọn tất cả các cột hoặc thậm chí chỉ là dữ liệu XML (hiện tại VARBINARY) nhanh hơn, nhưng thực sự làm tổn thương các truy vấn không chọn dữ liệu "XML". Giả sử bạn có khoảng 50 byte trong các cột khác và có FILLFACTOR100, thì:

  • Không nén: 15k XMLdữ liệu cần 2 trang LOB_DATA, sau đó yêu cầu 2 con trỏ cho Root nội tuyến. Con trỏ đầu tiên là 24 byte và thứ hai là 12, với tổng số 36 byte được lưu liên tiếp cho dữ liệu XML. Tổng kích thước hàng là 86 byte và bạn có thể điều chỉnh khoảng 93 hàng trong số đó trên trang dữ liệu 8060 byte. Do đó, 1 triệu hàng yêu cầu 10,753 trang dữ liệu.

  • Nén tùy chỉnh: 2,5k VARBINARYdữ liệu sẽ phù hợp với hàng. Tổng kích thước hàng là 2610 (2,5 * 1024 = 2560) byte và bạn chỉ có thể vừa 3 hàng trong số đó trên trang dữ liệu 8060 byte. Do đó, 1 triệu hàng yêu cầu 333.334 trang dữ liệu.

Ergo, thực hiện các kết quả nén tùy chỉnh trong việc tăng 30 lần trong các trang dữ liệu cho Chỉ mục cụm. Có nghĩa là, tất cả các truy vấn sử dụng một Clustered Index quét tại có khoảng 322.500 hơn các trang dữ liệu để đọc. Vui lòng xem phần chi tiết bên dưới để biết thêm các phân nhánh thực hiện kiểu nén này.

Tôi sẽ thận trọng chống lại bất kỳ tái cấu trúc dựa trên hiệu suất của SELECT TOP 1000 *. Đó không có khả năng là một truy vấn mà ứng dụng thậm chí sẽ đưa ra và không nên được sử dụng làm cơ sở duy nhất cho (các) tối ưu hóa không cần thiết.

Để biết thêm thông tin chi tiết và thử nghiệm nhiều hơn để thử, vui lòng xem phần bên dưới.


Câu hỏi này không thể được đưa ra một câu trả lời dứt khoát, nhưng ít nhất chúng ta có thể đạt được một số tiến bộ và đề xuất nghiên cứu bổ sung để giúp chúng ta tiến gần hơn để tìm ra vấn đề chính xác (lý tưởng dựa trên bằng chứng).

Những gì chúng ta biết:

  1. Bảng có khoảng 1 triệu hàng
  2. Kích thước bảng xấp xỉ 15 GB
  3. Bảng chứa một XMLcột và một vài cột khác của các loại: INT, BIGINT, UNIQUEIDENTIFIER, "vv"
  4. XMLcột "kích thước" là, trung bình khoảng 15k
  5. Sau khi chạy DBCC DROPCLEANBUFFERS, phải mất 20 - 25 giây để hoàn thành truy vấn sau:SELECT TOP 1000 * FROM TABLE
  6. Chỉ số cụm đang được quét
  7. Phân mảnh trên Chỉ số cụm là gần 0%

Những gì chúng tôi nghĩ rằng chúng tôi biết:

  1. Không có hoạt động đĩa khác ngoài các truy vấn này. Bạn có chắc không? Ngay cả khi không có truy vấn người dùng khác, có hoạt động nền diễn ra không? Có các quy trình bên ngoài để SQL Server chạy trên cùng một máy có thể chiếm một số IO không? Có thể không có, nhưng nó không rõ ràng chỉ dựa trên thông tin được cung cấp.
  2. 15 MB dữ liệu XML đang được trả lại. Con số này dựa trên cái gì? Một ước tính xuất phát từ 1000 hàng nhân trung bình 15k dữ liệu XML mỗi hàng? Hoặc tổng hợp theo chương trình những gì đã nhận được cho truy vấn đó? Nếu nó chỉ là một ước tính, tôi sẽ không dựa vào nó vì việc phân phối dữ liệu XML có thể thậm chí không theo cách được ngụ ý bởi một mức trung bình đơn giản.
  3. Nén XML có thể giúp. Làm thế nào chính xác bạn sẽ thực hiện nén trong .NET? Thông qua các lớp GZipStream hoặc DeflateStream ? Đây không phải là một lựa chọn chi phí bằng không. Nó chắc chắn sẽ nén một số dữ liệu theo một tỷ lệ lớn, nhưng nó cũng sẽ cần nhiều CPU hơn vì bạn sẽ cần một quy trình bổ sung để nén / giải nén dữ liệu mỗi lần. Kế hoạch này cũng sẽ loại bỏ hoàn toàn khả năng của bạn:

    • truy vấn dữ liệu XML thông qua .nodes, .value, .query, và .modifychức năng XML.
    • lập chỉ mục dữ liệu XML.

      Xin lưu ý (vì bạn đã đề cập rằng XML là "rất dư thừa") rằng XMLkiểu dữ liệu đã được tối ưu hóa ở chỗ nó lưu trữ các tên thành phần và thuộc tính trong từ điển, gán ID chỉ mục số nguyên cho từng mục và sau đó sử dụng ID số nguyên đó trong toàn bộ tài liệu (do đó nó không lặp lại tên đầy đủ cho mỗi lần sử dụng và cũng không lặp lại nó dưới dạng thẻ đóng cho các thành phần). Các dữ liệu thực tế cũng có không gian trắng bên ngoài được loại bỏ. Đây là lý do tại sao các tài liệu XML được trích xuất không giữ lại cấu trúc ban đầu của chúng và tại sao các phần tử trống trích xuất <element />ngay cả khi chúng đi vào như<element></element>. Vì vậy, bất kỳ lợi ích nào từ việc nén qua GZip (hoặc bất cứ thứ gì khác) sẽ chỉ được tìm thấy bằng cách nén các giá trị phần tử và / hoặc thuộc tính, có diện tích bề mặt nhỏ hơn nhiều có thể được cải thiện hơn hầu hết mong đợi và rất có thể không đáng để mất khả năng như đã lưu ý trực tiếp ở trên.

      Cũng xin lưu ý rằng việc nén dữ liệu XML và lưu trữ VARBINARY(MAX)kết quả sẽ không loại bỏ quyền truy cập LOB, nó sẽ làm giảm dữ liệu. Tùy thuộc vào kích thước của phần còn lại của dữ liệu trên hàng, giá trị nén có thể vừa với hàng hoặc nó vẫn có thể yêu cầu các trang LOB.

Thông tin đó, trong khi hữu ích, gần như không đủ. Có rất nhiều yếu tố ảnh hưởng đến hiệu suất truy vấn, vì vậy chúng tôi cần một bức tranh chi tiết hơn nhiều về những gì đang diễn ra.

Những gì chúng ta không biết, nhưng cần phải:

  1. Tại sao hiệu suất của SELECT *vật chất? Đây có phải là một mẫu mà bạn sử dụng trong mã. Nếu vậy, tại sao?
  2. Hiệu suất của việc chỉ chọn cột XML là gì? Số liệu thống kê và thời gian nếu bạn làm chỉ : SELECT TOP 1000 XmlColumn FROM TABLE;?
  3. Mất bao nhiêu trong 20 - 25 giây để trả lại 1000 hàng này có liên quan đến các yếu tố mạng (lấy dữ liệu qua dây) và mức độ liên quan đến các yếu tố máy khách (hiển thị khoảng 15 MB cộng với phần còn lại của không Dữ liệu XML vào lưới trong SSMS, hoặc có thể lưu vào đĩa)?

    Việc bao gồm hai khía cạnh của hoạt động đôi khi có thể được thực hiện bằng cách đơn giản là không trả lại dữ liệu. Bây giờ, người ta có thể nghĩ rằng chọn vào Bảng tạm thời hoặc Biến bảng, nhưng điều này sẽ chỉ giới thiệu một vài biến mới (ví dụ: I / O đĩa tempdb, ghi nhật ký giao dịch, có thể tự động tăng trưởng dữ liệu tempdb và / hoặc tệp nhật ký không gian trong vùng đệm, v.v.). Tất cả những yếu tố mới thực sự có thể làm tăng thời gian truy vấn. Thay vào đó, tôi thường lưu trữ các cột thành các biến (của kiểu dữ liệu thích hợp; không SQL_VARIANT) được ghi đè lên với mỗi hàng mới (nghĩa là SELECT @Column1 = tab.Column1,...).

    TUY NHIÊN , như đã được @PaulWhite chỉ ra trong DBA.StackExchange Q & A này, Logical đọc khác nhau khi truy cập cùng một dữ liệu LOB , với nghiên cứu bổ sung của riêng tôi được đăng trên PasteBin ( tập lệnh T-SQL để kiểm tra các kịch bản khác nhau cho các lần đọc LOB ) , LOB không được truy cập một cách nhất quán giữa SELECT, SELECT INTO, SELECT @XmlVariable = XmlColumn, SELECT @XmlVariable = XmlColumn.query(N'/'), và SELECT @NVarCharVariable = CONVERT(NVARCHAR(MAX), XmlColumn). Vì vậy, các tùy chọn của chúng tôi bị giới hạn hơn một chút ở đây, nhưng đây là những gì có thể được thực hiện:

    1. Loại trừ các sự cố mạng bằng cách thực hiện truy vấn trên máy chủ đang chạy SQL Server, trong SSMS hoặc SQLCMD.EXE.
    2. Loại trừ các sự cố của máy khách trong SSMS bằng cách đi tới Tùy chọn truy vấn -> Kết quả -> Lưới và kiểm tra tùy chọn cho "Hủy kết quả sau khi thực hiện". Xin lưu ý rằng tùy chọn này sẽ ngăn TẤT CẢ đầu ra, bao gồm các thông báo, nhưng vẫn có thể hữu ích để loại trừ thời gian SSMS cần phân bổ bộ nhớ cho mỗi hàng và sau đó vẽ nó vào lưới.
      Ngoài ra, bạn có thể thực hiện truy vấn thông qua SQLCMD.EXE và hướng đầu ra đi đến đâu thông qua : -o NUL:.
  4. Có Loại Chờ liên quan đến truy vấn này không? Nếu có, Loại Chờ đó là gì?
  5. Có gì là thực tế kích thước dữ liệu cho các XMLcột được trả lại ? Kích thước trung bình của cột đó trên toàn bộ bảng không thực sự quan trọng nếu các hàng "TOP 1000" chứa một phần lớn không tương xứng trong tổng số XMLdữ liệu. Nếu bạn muốn biết về TOP 1000 hàng, hãy nhìn vào những hàng đó. Vui lòng chạy như sau:

    SELECT TOP 1000 tab.*,
           SUM(DATALENGTH(tab.XmlColumn)) / 1024.0 AS [TotalXmlKBytes],
           AVG(DATALENGTH(tab.XmlColumn)) / 1024.0 AS [AverageXmlKBytes]
           STDEV(DATALENGTH(tab.XmlColumn)) / 1024.0 AS [StandardDeviationForXmlKBytes]
    FROM   SchemaName.TableName tab;
  6. Các chính xác schema bảng. Vui lòng cung cấp báo cáo đầy đủ CREATE TABLE , bao gồm tất cả các chỉ mục.
  7. Kế hoạch truy vấn? Đó có phải là một cái gì đó mà bạn có thể đăng? Thông tin đó có thể sẽ không thay đổi bất cứ điều gì, nhưng tốt hơn là nên biết rằng nó sẽ không thay vì đoán rằng nó sẽ không sai và;
  8. Có sự phân mảnh vật lý / bên ngoài trên tệp dữ liệu? Mặc dù điều này có thể không phải là một yếu tố lớn ở đây, vì bạn đang sử dụng "SATA cấp độ người tiêu dùng" và không phải SSD hoặc thậm chí là siêu đắt tiền, hiệu ứng của các lĩnh vực được đặt hàng tối ưu sẽ đáng chú ý hơn, đặc biệt là số lượng các lĩnh vực đó cần phải đọc tăng lên.
  9. Các kết quả chính xác của truy vấn sau đây là gì:

    SELECT * FROM sys.dm_db_index_physical_stats(DB_ID(),
                              OBJECT_ID(N'dbo.SchemaName.TableName'), 1, 0, N'LIMITED');

CẬP NHẬT

Nó xảy ra với tôi rằng tôi nên thử tái tạo kịch bản này để xem liệu tôi có trải nghiệm hành vi tương tự không. Vì vậy, tôi đã tạo một bảng có nhiều cột (tương tự như mô tả mơ hồ trong Câu hỏi) và sau đó điền vào đó 1 triệu hàng và cột XML có khoảng 15k dữ liệu mỗi hàng (xem mã bên dưới).

Những gì tôi tìm thấy là thực hiện SELECT TOP 1000 * FROM TABLEhoàn thành trong 8 giây lần đầu tiên và 2 - 4 giây mỗi lần sau đó (vâng, thực hiện DBCC DROPCLEANBUFFERStrước mỗi lần chạy SELECT *truy vấn). Và máy tính xách tay vài năm tuổi của tôi không nhanh: SQL Server 2012 SP2 Developer Edition, 64 bit, RAM 6 GB, lõi kép 2,5 Ghz Core i5 và ổ đĩa SATA 5400 RPM. Tôi cũng đang chạy SSMS 2014, SQL Server Express 2014, Chrome và một số thứ khác.

Dựa trên thời gian phản hồi của hệ thống của tôi, tôi sẽ nhắc lại rằng chúng tôi cần thêm thông tin (ví dụ cụ thể về bảng và dữ liệu, kết quả của các thử nghiệm được đề xuất, v.v.) để giúp thu hẹp nguyên nhân của thời gian phản hồi 20 - 25 giây mà bạn đang thấy

SET ANSI_NULLS, NOCOUNT ON;
GO

IF (OBJECT_ID(N'dbo.XmlReadTest') IS NOT NULL)
BEGIN
    PRINT N'Dropping table...';
    DROP TABLE dbo.XmlReadTest;
END;

PRINT N'Creating table...';
CREATE TABLE dbo.XmlReadTest 
(
    ID INT NOT NULL IDENTITY(1, 1),
    Col2 BIGINT,
    Col3 UNIQUEIDENTIFIER,
    Col4 DATETIME,
    Col5 XML,
    CONSTRAINT [PK_XmlReadTest] PRIMARY KEY CLUSTERED ([ID])
);
GO

DECLARE @MaxSets INT = 1000,
        @CurrentSet INT = 1;

WHILE (@CurrentSet <= @MaxSets)
BEGIN
    RAISERROR(N'Populating data (1000 sets of 1000 rows); Set # %d ...',
              10, 1, @CurrentSet) WITH NOWAIT;
    INSERT INTO dbo.XmlReadTest (Col2, Col3, Col4, Col5)
        SELECT  TOP 1000
                CONVERT(BIGINT, CRYPT_GEN_RANDOM(8)),
                NEWID(),
                GETDATE(),
                N'<test>'
                  + REPLICATE(CONVERT(NVARCHAR(MAX), CRYPT_GEN_RANDOM(1), 2), 3750)
                  + N'</test>'
        FROM        [master].[sys].all_columns sac1;

    IF ((@CurrentSet % 100) = 0)
    BEGIN
        RAISERROR(N'Executing CHECKPOINT ...', 10, 1) WITH NOWAIT;
        CHECKPOINT;
    END;

    SET @CurrentSet += 1;
END;

--

SELECT COUNT(*) FROM dbo.XmlReadTest; -- Verify that we have 1 million rows

-- O.P. states that the "clustered index fragmentation is close to 0%"
ALTER INDEX [PK_XmlReadTest] ON dbo.XmlReadTest REBUILD WITH (FILLFACTOR = 90);
CHECKPOINT;

--

DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS;

SET STATISTICS IO, TIME ON;
SELECT TOP 1000 * FROM dbo.XmlReadTest;
SET STATISTICS IO, TIME OFF;

/*
Scan count 1, logical reads 21,       physical reads 1,     read-ahead reads 4436,
              lob logical reads 5676, lob physical reads 1, lob read-ahead reads 3967.

 SQL Server Execution Times:
   CPU time = 171 ms,  elapsed time = 8329 ms.
*/

Và, vì chúng tôi muốn tính thời gian đọc các trang không phải LOB, tôi đã chạy truy vấn sau để chọn tất cả trừ cột XML (một trong những thử nghiệm tôi đã đề xuất ở trên). Điều này trở lại trong 1,5 giây khá nhất quán.

DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS;

SET STATISTICS IO, TIME ON;
SELECT TOP 1000 ID, Col2, Col3, Col4 FROM dbo.XmlReadTest;
SET STATISTICS IO, TIME OFF;

/*
Scan count 1, logical reads 21,    physical reads 1,     read-ahead reads 4436,
              lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

 SQL Server Execution Times:
   CPU time = 0 ms,  elapsed time = 1666 ms.
*/

Kết luận (hiện tại)
Dựa trên nỗ lực tái tạo kịch bản của bạn, tôi không nghĩ rằng chúng ta có thể chỉ ra ổ đĩa SATA hoặc I / O không tuần tự là nguyên nhân chính của 20 - 25 giây, đặc biệt là vì chúng ta vẫn không biết truy vấn trả về nhanh như thế nào khi không bao gồm cột XML. Và tôi đã không thể sao chép số lượng lớn các lần đọc logic (không phải LOB) mà bạn đang hiển thị, nhưng tôi có cảm giác rằng tôi cần thêm nhiều dữ liệu vào mỗi hàng theo điều đó tuyên bố về:

~ 90% trang bảng là LOB_DATA

Bảng của tôi có 1 triệu hàng, mỗi hàng chỉ có hơn 15k dữ liệu XML và sys.dm_db_index_physical_statscho thấy có 2 triệu trang LOB_DATA. 10% còn lại sau đó sẽ là 222k trang dữ liệu IN_law, nhưng tôi chỉ có 11.630 trang. Vì vậy, một lần nữa, chúng ta cần thêm thông tin về lược đồ bảng thực tế và dữ liệu thực tế.


Cuộc thảo luận này đã được chuyển sang trò chuyện .
Paul White nói GoFundMonica

10

Tôi có đúng không khi nghĩ rằng các trang LOB_DATA có thể gây ra các bản quét chậm không chỉ vì kích thước của chúng mà còn vì SQL Server không thể quét chỉ mục được phân cụm một cách hiệu quả

Có, việc đọc dữ liệu LOB không được lưu liên tiếp dẫn đến IO ngẫu nhiên thay vì IO liên tiếp. Số liệu hiệu suất đĩa để sử dụng ở đây để hiểu tại sao nó nhanh hay chậm là IOPS đọc ngẫu nhiên.

Dữ liệu LOB được lưu trữ trong cấu trúc cây trong đó trang dữ liệu trong chỉ mục được nhóm trỏ đến trang Dữ liệu LOB có cấu trúc gốc LOB lần lượt trỏ đến dữ liệu LOB thực tế. Khi duyệt qua các nút gốc trong chỉ mục được phân cụm, SQL Server chỉ có thể nhận được dữ liệu liên tiếp bằng cách đọc tuần tự. Để có được dữ liệu LOB, SQL Server phải đi nơi khác trên đĩa.

Tôi đoán rằng nếu bạn đổi sang đĩa SSD, bạn sẽ không phải chịu đựng nhiều như vậy vì IOPS ngẫu nhiên cho SSD cao hơn nhiều so với đĩa quay.

nó có được coi là hợp lý để có một cấu trúc bảng / mẫu dữ liệu như vậy không?

Vâng, nó có thể. Phụ thuộc vào những gì bảng này đang làm cho bạn.

Thông thường, các vấn đề về hiệu năng với XML trong SQL Server xảy ra khi bạn muốn sử dụng T-SQL để truy vấn XML và thậm chí còn hơn thế khi bạn muốn sử dụng các giá trị từ XML trong một vị ngữ trong mệnh đề where hoặc tham gia. Nếu đó là trường hợp bạn có thể có một cái nhìn về quảng cáo thuộc tính hoặc các chỉ mục XML chọn lọc hoặc thiết kế lại các cấu trúc bảng của bạn thay đổi XML thành các bảng thay thế.

Tôi đã thử nén

Tôi đã làm điều đó một lần trong một sản phẩm hơn 10 năm trước và đã hối hận kể từ đó. Tôi thực sự đã bỏ lỡ việc không thể làm việc với dữ liệu bằng T-SQL, vì vậy tôi sẽ không đề xuất điều đó cho bất cứ ai nếu có thể tránh được.


Cảm ơn rất nhiều cho câu trả lời. Về nén: Tôi không chắc liệu đề xuất chống nghiêm ngặt như vậy có hợp lý hay không, vì nhu cầu thực sự truy vấn dữ liệu từ T-SQL rõ ràng phụ thuộc vào bản chất của dữ liệu được lưu trữ. Trong trường hợp của tôi, tôi quyết định đi với nén ngay bây giờ.
Alexander Shelemin
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.