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

SQL Server:Mặt tối của NVARCHAR

Giới thiệu

Trong bài viết này, chúng ta sẽ nói về việc sử dụng nvarchar loại dữ liệu. Chúng ta sẽ khám phá cách SQL Server lưu trữ kiểu dữ liệu này trên đĩa và cách nó được xử lý trong RAM. Chúng tôi cũng sẽ kiểm tra kích thước của nvarchar có thể ảnh hưởng đến hiệu suất như thế nào.

Kích thước dữ liệu thực tế:nchar so với nvarchar

Chúng tôi sử dụng nvarchar khi kích thước của các mục dữ liệu cột có thể sẽ thay đổi đáng kể. Kích thước lưu trữ (tính bằng byte) lớn gấp đôi chiều dài thực của dữ liệu được nhập + 2 byte. Điều này cho phép chúng tôi tiết kiệm bộ nhớ đĩa so với việc sử dụng nchar loại dữ liệu. Chúng ta hãy xem xét ví dụ sau. Chúng tôi đang tạo hai bảng. Một bảng chứa cột nvarchar, bảng khác chứa các cột nchar. Kích thước của cột là 2000 ký tự (4000 byte).

CREATE TABLE dbo.testnvarchar (
  col1 NVARCHAR(2000) NULL
);
GO

INSERT INTO dbo.testnvarchar (col1)
  SELECT
    REPLICATE('&', 10)
GO 

CREATE TABLE dbo.testnchar (
  col1 NCHAR(2000) NULL
);
GO

INSERT INTO dbo.testnchar (col1)
  SELECT
    REPLICATE('&', 10)
GO 

Kích thước hàng thực tế là:

Như chúng ta có thể thấy, kích thước hàng thực tế của kiểu dữ liệu nvarchar nhỏ hơn nhiều so với kiểu dữ liệu nchar. Trong trường hợp của kiểu dữ liệu nchar, chúng tôi sử dụng ~ 4000 byte để lưu trữ chuỗi ký tự 10 ký hiệu. Chúng tôi sử dụng ~ 20 byte để lưu trữ cùng một chuỗi ký tự trong trường hợp kiểu dữ liệu nvarchar.

Công cụ SQL Server xử lý dữ liệu vào RAM (vùng đệm). Còn về kích thước hàng trong bộ nhớ?

Kích thước dữ liệu thực tế:HDD và RAM

Hãy thực hiện truy vấn sau:

SELECT col1 FROM dbo.testnchar;

Không có sự khác biệt giữa việc sử dụng đĩa và RAM trong trường hợp chuỗi ký tự có độ dài cố định.

SELECT col1 FROM dbo.testnvarchar;

Chúng ta có thể thấy rằng SQL Server Engine yêu cầu bộ nhớ chỉ bằng một nửa kích thước hàng đã khai báo (2000 byte thay vì 20 byte thực tế) và một vài byte cho một thông tin bổ sung. Từ một phía, chúng tôi giảm mức sử dụng không gian đĩa nhưng từ một phía khác, chúng tôi có thể tăng dung lượng RAM được yêu cầu. Đây là một tác dụng phụ của việc sử dụng các kiểu dữ liệu ký tự khác nhau. Tác dụng phụ này có thể ảnh hưởng nặng nề đến tài nguyên trong một số trường hợp.

FORMAT ():RAM được yêu cầu so với RAM được sử dụng

Chúng tôi sử dụng hàm FORMAT, hàm này trả về giá trị được định dạng với định dạng được chỉ định và văn hóa tùy chọn. Giá trị trả về là nvarchar hoặc null. Độ dài của giá trị trả về được xác định bởi định dạng . FORMAT (getdate (), ‘yyyyMMdd’, ’en-US’) sẽ cho kết quả là ‘20170412’. Chúng ta cần 16 byte để lưu trữ kết quả này trên cột trên đĩa (kết quả sẽ là nvarchar (8)). Kích thước dữ liệu trong RAM cho dữ liệu cụ thể là bao nhiêu?

Hãy thực hiện truy vấn sau. Chúng tôi sử dụng môi trường sau:

  • AdventureWorks2014
  • Phiên bản phát triển MS SQL 2016
  • dbo.Customer (19’820’000 bản ghi) chứa dữ liệu từ Sales.Customer (19’820 bản ghi đã được tải lên 1000 lần)):
;WITH rs
AS
(SELECT
    c.customerid
   ,c.modifieddate
   ,p.LastName
  FROM [dbo].[Customer] c
  LEFT OUTER JOIN [person].[person] p
    ON p.BusinessEntityID = c.PersonID)
SELECT
  customerid
 ,LastName
 ,FORMAT([modifieddate], 'yyyyMMdd', 'en-US') AS md
 ,' ' AS code INTO #tmp
FROM rs

Kế hoạch thực thi Truy vấn khá đơn giản:

Thao tác đầu tiên là “Quét chỉ mục theo cụm” trên bảng dbo.Customer. ~ 19 000 000 bản ghi đã được đọc. Kích thước dữ liệu ước tính là 435 Mb.

Thao tác tiếp theo là “Compute Scalar” (tính toán hàm FORMAT ()). Kết quả khá bất ngờ khi chúng tôi định dạng chuỗi ký tự 16 byte. Kích thước hàng đã tăng đáng kể từ 23 byte lên 4019 byte. Tương tự với Kích thước dữ liệu ước tính - từ 435 MB đến 74 GB. Chúng ta có thể thấy rằng FORMAT () trả về NVARCHAR (4000).

MS SQL Server 2016 có khả năng tuyệt vời để hiển thị cấp bộ nhớ quá mức. Chúng ta có thể thấy cảnh báo trong thao tác cuối cùng (T-SQL CHỌN VÀO):

Đây là bộ nhớ được cấp "quá mức":hơn 90% bộ nhớ được cấp không được sử dụng.

Thống kê thời gian truy vấn là:

Thời gian thực thi dài tùy thuộc vào việc thực thi hàm vô hướng không hiệu quả và tác dụng phụ của Cấp bộ nhớ Quá mức - Kết hợp băm (Nối bên ngoài bên phải). Chúng ta có một tác động tích lũy do hai nguyên nhân khác nhau:thực thi nhiều hàm vô hướng và cấp quá nhiều bộ nhớ.

Công cụ SQL Server có thể cấp không quá 25% bộ nhớ được phép cho mỗi truy vấn. Chúng tôi có thể thay đổi số tiền này trong phiên bản doanh nghiệp của MS SQL Server bằng cách sử dụng trình quản lý tài nguyên. Bộ nhớ được cấp bao gồm hai phần:bắt buộc và bổ sung. Một bộ nhớ bắt buộc được sử dụng cho nhu cầu bên trong - cho các hoạt động sắp xếp và kết hợp băm. Bộ nhớ bổ sung dựa trên Kích thước dữ liệu ước tính. Nếu cả bộ nhớ bắt buộc và bộ nhớ bổ sung vượt quá giới hạn 25%, công cụ SQL Server cấp thêm 25% bộ nhớ khả dụng. Đọc bài đăng cấp bộ nhớ SQL Server để biết chi tiết.

Hãy thực hiện cùng một truy vấn mà không có hàm FORMAT ().

;WITH rs
AS
(SELECT
    c.customerid
   ,c.modifieddate
   ,p.LastName
  FROM [dbo].[Customer] c
  LEFT OUTER JOIN [person].[person] p
    ON p.BusinessEntityID = c.PersonID)
SELECT
  customerid
 ,LastName
 ,' ' AS code INTO #tmp
FROM rs

Chúng ta có thể thấy một triển khai Right Outer Join khác (Merge Join thay vì Hash Join).

Thông tin Cấp bộ nhớ là (nếu không có Sắp xếp và Máy chủ SQL Tham gia băm có thể không cấp bộ nhớ):

Thời gian truy vấn Thống kê là (thời gian giảm có thể đoán trước được:không thực thi chức năng vô hướng, Kích thước dữ liệu ước tính nhỏ hơn trong mẫu trước):

Vì vậy, chúng tôi đang tăng “bộ nhớ được cấp” lên đến 222 MB (và đang sử dụng ít hơn 2 MB trong số đó) bằng cách sử dụng hàm FORMAT (). Khối lượng dữ liệu trong ví dụ này nhỏ.

Truy vấn thực thi trong thời gian dài

Hãy xem xét truy vấn SQL thực từ môi trường sản xuất. Truy vấn này đã được thực thi trong quá trình tải hàng loạt (không phải là một kịch bản giao dịch cổ điển). Chúng tôi sử dụng MS SQL Server bắt đầu trên Amazon Web Services (AWS, Amazon Relational Database Service). Đặc điểm của phiên bản DB là 160 GB RAM (có thể cấp không quá 30 GB RAM cho mỗi truy vấn) và 40 vCPU. Truy vấn SQL gần giống như ví dụ trên (sự khác biệt là số lượng bảng và kích thước dữ liệu):CTE bao gồm phép nối giữa 6 bảng. “Bảng chính” (một bảng trong mệnh đề FROM) chứa ~ 175’000’000 bản ghi và kích thước dữ liệu là 20GB. Các bảng Tra cứu (bảng bên phải trong mệnh đề JOIN) nhỏ (so với bảng chính). Truy vấn SQL chứa hai lệnh gọi hàm FORMAT () (hai cột từ bảng "master table" là tham số của hàm này).

Truy vấn sản xuất trông giống như sau:

;WITH rs AS
(
SELECT 
<in column list>,
c.modifieddate, 
c.createddate
FROM [Master table] c
	LEFT OUTER JOIN [table1 ] p1 ON …
	LEFT OUTER JOIN [table2 ] p2 ON …
	LEFT OUTER JOIN [table3 ] p3 ON …
	LEFT OUTER JOIN [table4 ] p4 ON …
	LEFT OUTER JOIN [table5 ] p5 ON …
)
SELECT DISTINT
<out column list>,
FORMAT([modifieddate], 'yyyyMMdd','en-US') AS md,
FORMAT([createddate], 'yyyyMMdd','en-US') AS cd
INTO #tmp
FROM rs

"Hình ảnh" của kế hoạch thực thi là bên dưới (kế hoạch thực hiện rất đơn giản:nối và sắp xếp tuần tự (các từ khóa DISTINCT) ở trên cùng):

Hãy cùng chúng tôi khám phá thông tin một cách chi tiết.

Thao tác đầu tiên là “Quét bảng” (tất cả đều đúng, không có gì ngạc nhiên):

Thao tác “Tính toán vô hướng” làm tăng đáng kể Kích thước hàng ước tính cũng như Kích thước hàng ước tính (từ 19 GB lên đến 1,3 TB). Hai lệnh gọi của hàm FORMAT () đã thêm khoảng 8000 byte vào Kích thước hàng ước tính (nhưng kích thước dữ liệu thực tế nhỏ hơn).

Một trong các phép toán JOIN (Hash Match, Right Outer Join) sử dụng các cột không phải là duy nhất từ ​​bảng bên phải. Nó không quan trọng trong trường hợp của một vài bản ghi. Đây không phải là trường hợp của chúng tôi. Do đó, Kích thước dữ liệu ước tính đang tăng lên đến ~ 2,4TB.

Cũng có cảnh báo (không có đủ RAM để xử lý hoạt động này):

Truy vấn SQL có chứa thao tác “Sắp xếp phân biệt” ở trên cùng, trông giống như quả anh đào trên đầu chiếc bánh. Chúng ta có thể thấy cảnh báo tương tự ở đó.

Kết quả của việc sử dụng một hàm vô hướng là thời gian thực thi truy vấn rất lâu:24 giờ. Một trong những nguyên nhân của vấn đề này là ước tính không chính xác về kích thước dữ liệu được yêu cầu dựa trên "Kích thước dữ liệu ước tính". Không sử dụng hàm FORMAT (), MS SQL Server thực hiện truy vấn này sau 2 giờ.

Kết luận

Các nhà phát triển nên cẩn thận khi sử dụng các kiểu dữ liệu nvarchar và varchar. Việc chọn các kiểu dữ liệu dư thừa cho các cột có thể dẫn đến việc tăng dung lượng bộ nhớ cần thiết. Kết quả là RAM sẽ bị lãng phí, hiệu suất cơ sở dữ liệu sẽ bị giảm sút.


  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ông cụ miễn phí để so sánh hai Cơ sở dữ liệu SQL Server là gì?

  2. Ví dụ về SQRT () trong SQL Server

  3. Làm cách nào để bạn cắt ngắn tất cả các bảng trong cơ sở dữ liệu bằng TSQL?

  4. SQL Server SMO phàn nàn về việc thiếu DLL

  5. Nhóm DateTime thành các khoảng thời gian 5,15,30 và 60 phút