Vâng, máy tính xách tay cũ của tôi mạnh hơn nhiều lần so với máy chủ sản xuất của bạn.

Đây chính xác là những lời phàn nàn mà tôi đã nghe được từ các nhà phát triển của chúng tôi. Điều thú vị nhất là điều này hóa ra lại là sự thật, dẫn đến một cuộc điều tra kéo dài. Chúng ta sẽ nói về các máy chủ SQL chạy trên VMware.

Vâng, máy tính xách tay cũ của tôi mạnh hơn nhiều lần so với máy chủ sản xuất của bạn.

Trên thực tế, thật dễ dàng để đảm bảo rằng máy chủ sản xuất nằm phía sau máy tính xách tay một cách vô vọng. Thực thi (không phải trên tempdb và không phải trên cơ sở dữ liệu có bật Độ bền trễ) mã:

set nocount on
create table _t (v varchar(100))
declare @n int=300000
while @n>0 begin 
  insert into _t select 'What a slowpoke!'
  delete from _t
  set @n=@n-1
  end
GO
drop table _t

Trên máy tính để bàn của tôi mất 5 giây và trên máy chủ sản xuất mất 28 giây. Bởi vì SQL phải đợi sự kết thúc vật lý của mục nhập nhật ký giao dịch và chúng tôi đang thực hiện các giao dịch rất ngắn ở đây. Nói một cách đại khái, chúng tôi đã lái một chiếc xe tải lớn, mạnh mẽ tham gia giao thông trong thành phố và chứng kiến ​​​​cảnh những người giao bánh pizza trên xe tay ga vượt qua nó một cách nhanh chóng - thông lượng ở đây không quan trọng, chỉ có độ trễ là quan trọng. Và không có bộ lưu trữ mạng nào, cho dù giá của nó có bao nhiêu số XNUMX, có thể đánh bại ổ SSD cục bộ về độ trễ.

(trong các bình luận, hóa ra là tôi đã nói dối - tôi đã bị trì hoãn độ bền ở cả hai nơi. Không có độ bền bị trì hoãn thì hóa ra:
Máy tính để bàn - 39 giây, 15K tr/giây, 0.065 ms/io khứ hồi
SẢN XUẤT - 360 giây, 1600 tr/giây, 0.6ms
Lẽ ra tôi phải nhận thấy rằng nó quá nhanh)

Tuy nhiên, trong trường hợp này chúng ta đang xử lý các số 3 tầm thường của hàm Riemann zeta bằng một ví dụ tầm thường. Trong ví dụ mà các nhà phát triển mang đến cho tôi, nó lại khác. Tôi tin rằng họ đúng và bắt đầu loại bỏ khỏi ví dụ tất cả các chi tiết cụ thể liên quan đến logic kinh doanh. Tại một thời điểm nào đó, tôi nhận ra rằng tôi hoàn toàn có thể vứt bỏ mã của họ và viết mã của riêng mình - điều này chứng tỏ vấn đề tương tự - trong quá trình sản xuất, nó chạy chậm hơn 4-XNUMX lần:

create function dbo.isPrime (@n bigint)
returns int
as
  begin
  if @n = 1 return 0
  if @n = 2 return 1
  if @n = 3 return 1
  if @n % 2 = 0 return 0
  declare @sq int
  set @sq = sqrt(@n)+1 -- check odds up to sqrt
  declare @dv int = 1
  while @dv < @sq 
    begin
	set @dv=@dv+2
	if @n % @dv = 0 return 0
	end
  return 1
  end
GO
declare @dt datetime set @dt=getdate()
select dbo.isPrime(1000000000000037)
select datediff(ms,@dt,getdate()) as ms
GO

Nếu mọi thứ đều ổn thì việc kiểm tra tính nguyên tố của một số sẽ mất 6-7-8 giây. Điều này đã xảy ra trên một số máy chủ. Nhưng trên một số, quá trình kiểm tra mất 25-40 giây. Điều thú vị là không có máy chủ nào mà quá trình thực thi sẽ mất tới 14 giây - mã hoạt động rất nhanh hoặc rất chậm, tức là, vấn đề là, giả sử là đen trắng.

Những gì tôi đã làm? Số liệu VMware được sử dụng. Mọi thứ đều ổn ở đó - có rất nhiều tài nguyên, Thời gian sẵn sàng = 0, có đủ mọi thứ, trong quá trình thử nghiệm trên cả máy chủ nhanh và chậm CPU = 100 trên một vCPU. Tôi đã làm bài kiểm tra để tính số Pi - bài kiểm tra cho kết quả tương tự trên bất kỳ máy chủ nào. Mùi ma thuật đen ngày càng mạnh hơn.

Khi đến trang trại DEV, tôi bắt đầu chơi với các máy chủ. Hóa ra vMotion từ máy chủ này sang máy chủ khác có thể “chữa bệnh” cho một máy chủ, nhưng nó cũng có thể biến máy chủ “nhanh” thành máy chủ “chậm”. Có vẻ như thế này - một số máy chủ có vấn đề... nhưng... không. Một số máy ảo chạy chậm trên máy chủ, chẳng hạn như A, nhưng hoạt động nhanh trên máy chủ B. Và ngược lại, một máy ảo khác hoạt động nhanh trên máy chủ A và chậm lại trên máy chủ B! Cả máy “nhanh” và “chậm” đều thường xuyên quay trên máy chủ!

Kể từ lúc đó, trong không khí có mùi lưu huỳnh đặc trưng. Rốt cuộc, sự cố không thể do máy ảo (ví dụ: các bản vá Windows) - xét cho cùng, nó đã trở nên “nhanh” với vMotion. Nhưng vấn đề cũng không thể do máy chủ - xét cho cùng, nó có thể có cả máy “nhanh” và máy “chậm”. Ngoài ra, điều này không liên quan đến tải - tôi đã cố gắng đưa một máy "chậm" vào máy chủ, nơi không có gì khác ngoài nó.

Vì tuyệt vọng, tôi đã khởi chạy Process Explorer từ Sysinternals và xem xét ngăn xếp SQL. Trên các máy chậm, dòng này ngay lập tức thu hút sự chú ý của tôi:

ntoskrnl.exe!KeSynchronizeExecution+0x5bf6
ntoskrnl.exe!KeWaitForMultipleObjects+0x109d
ntoskrnl.exe!KeWaitForMultipleObjects+0xb3f
ntoskrnl.exe!KeWaitForSingleObject+0x377
ntoskrnl.exe!KeQuerySystemTimePrecise+0x881 < — !!!
ntoskrnl.exe!ObDereferenceObjectDeferDelete+0x28a
ntoskrnl.exe!KeSynchronizeExecution+0x2de2
sqllang.dll!CDiagThreadSafe::PxlvlReplace+0x1a20
... đã bỏ qua
sqldk.dll!SystemThread::MakeMiniSOSThread+0xa54
KERNEL32.DLL!BaseThreadInitThunk+0x14
ntdll.dll! RtlUserThreadStart + 0x21

Đây đã là một cái gì đó. Chương trình được viết:

    class Program
    {
        [DllImport("kernel32.dll")]
        static extern void GetSystemTimePreciseAsFileTime(out FILE_TIME lpSystemTimeAsFileTime);

        [StructLayout(LayoutKind.Sequential)]
        struct FILE_TIME
        {
            public int ftTimeLow;
            public int ftTimeHigh;
        }

        static void Main(string[] args)
        {
            for (int i = 0; i < 16; i++)
            {
                int counter = 0;

                var stopwatch = Stopwatch.StartNew();

                while (stopwatch.ElapsedMilliseconds < 1000)
                {
                    GetSystemTimePreciseAsFileTime(out var fileTime);
                    counter++;
                }

                if (i > 0)
                {
                    Console.WriteLine("{0}", counter);
                }
            }
        }
    }

Chương trình này thậm chí còn cho thấy sự chậm lại rõ rệt hơn - trên các máy “nhanh”, nó hiển thị 16-18 triệu chu kỳ mỗi giây, trong khi trên các máy chậm, nó hiển thị một triệu rưỡi, hoặc thậm chí 700 nghìn. Tức là chênh lệch là 10-20 lần (!!!). Đây đã là một chiến thắng nhỏ: trong mọi trường hợp, không có nguy cơ bị mắc kẹt giữa bộ phận hỗ trợ của Microsoft và VMware để họ quay mũi tên vào nhau.

Sau đó, tiến độ dừng lại - những kỳ nghỉ, những vấn đề quan trọng, cơn cuồng loạn do virus và khối lượng công việc tăng mạnh. Tôi thường đề cập đến vấn đề ma thuật với các đồng nghiệp của mình, nhưng đôi khi có vẻ như họ thậm chí không phải lúc nào cũng tin tôi - tuyên bố rằng VMware làm chậm mã tới 10-20 lần là quá quái dị.

Tôi cố gắng tự tìm hiểu điều gì đang làm tôi chậm lại. Đôi khi, đối với tôi, dường như tôi đã tìm ra giải pháp - bật và tắt phích cắm Nóng, thay đổi dung lượng bộ nhớ hoặc số lượng bộ xử lý thường biến máy thành máy “nhanh”. Nhưng không phải mãi mãi. Nhưng điều hóa ra lại đúng là chỉ cần đi ra ngoài và gõ vào tay lái - tức là thay đổi bất kỳ thông số máy ảo

Cuối cùng, các đồng nghiệp người Mỹ của tôi bất ngờ tìm ra nguyên nhân sâu xa.

Vâng, máy tính xách tay cũ của tôi mạnh hơn nhiều lần so với máy chủ sản xuất của bạn.

Các máy chủ khác nhau về tần số!

  • Theo quy định, đây không phải là một vấn đề lớn. Nhưng: khi chuyển từ máy chủ “bản địa” sang máy chủ có tần số “khác”, VMware phải điều chỉnh kết quả GetTimePrecise.
  • Theo quy định, đây không phải là vấn đề, trừ khi có ứng dụng yêu cầu thời gian chính xác hàng triệu lần mỗi giây, như máy chủ SQL.
  • Nhưng điều này không đáng sợ, vì máy chủ SQL không phải lúc nào cũng làm được điều này (xem phần Kết luận)

Nhưng có những trường hợp chiếc cào này đánh mạnh. Chưa hết, vâng, bằng cách chạm vào bánh xe (bằng cách thay đổi thứ gì đó trong cài đặt VM), tôi đã buộc VMware phải “tính toán lại” cấu hình và tần số của máy chủ hiện tại đã trở thành tần số “gốc” của máy.

phán quyết

www.vmware.com/files/pdf/techpaper/Timekeeping-In-VirtualMachines.pdf

Khi bạn tắt ảo hóa TSC, việc đọc TSC từ bên trong máy ảo sẽ trả về giá trị TSC của máy vật lý và việc ghi TSC từ bên trong máy ảo sẽ không có hiệu lực. Di chuyển máy ảo sang máy chủ khác, khôi phục nó từ trạng thái treo hoặc hoàn nguyên về ảnh chụp nhanh khiến TSC nhảy không liên tục. Một số hệ điều hành khách không khởi động được hoặc có các vấn đề về chấm công khác khi tắt tính năng ảo hóa TSC. Trước đây, tính năng này đôi khi được khuyến nghị để cải thiện hiệu suất của các ứng dụng đọc TSC thường xuyên., nhưng hiệu suất của TSC ảo đã được cải thiện đáng kể trong các sản phẩm hiện tại. Tính năng này cũng được khuyến nghị sử dụng khi thực hiện các phép đo yêu cầu nguồn thời gian thực chính xác trong máy ảo.

Tóm lại là bạn cần thêm tham số

Monitor_control.virtual_rdtsc = SAI

Kết luận

Chắc hẳn bạn có thắc mắc: tại sao SQL lại gọi GetTimePrecise thường xuyên như vậy?

Tôi không có mã nguồn máy chủ SQL, nhưng logic nói lên điều này. SQL gần như là một hệ điều hành có khả năng hợp tác đồng thời, trong đó mỗi luồng đôi khi phải “nhượng bộ”. Đâu là nơi tốt nhất để làm điều này? Nơi có sự chờ đợi tự nhiên - khóa hoặc IO. Được rồi, nhưng nếu chúng ta đang quay các vòng lặp tính toán thì sao? Khi đó, vị trí hiển nhiên và gần như duy nhất là trong trình thông dịch (đây không thực sự là một trình thông dịch), sau khi thực hiện câu lệnh tiếp theo.

Nói chung, máy chủ SQL không được sử dụng để tính toán thuần túy và đây không phải là vấn đề. Nhưng các vòng lặp hoạt động với tất cả các loại bảng tạm thời (được lưu vào bộ đệm ngay lập tức) sẽ biến mã thành một chuỗi các câu lệnh được thực thi rất nhanh.

Nhân tiện, nếu bạn gói chức năng này trong NATIVELY COMPILED thì nó sẽ ngừng hỏi thời gian và tốc độ của nó tăng lên gấp 10 lần. Còn đa nhiệm hợp tác thì sao? Nhưng đối với mã được biên dịch nguyên bản, chúng tôi phải thực hiện ĐA NĂNG TRƯỚC trong SQL.

Nguồn: www.habr.com

Thêm một lời nhận xét