นี่คือคำกล่าวอ้างที่ฉันได้ยินจากนักพัฒนาซอฟต์แวร์ของเรา สิ่งที่น่าสนใจที่สุดคือเรื่องนี้กลายเป็นจริง ทำให้เกิดการสืบสวนที่ยืดเยื้อ เราจะพูดถึงเซิร์ฟเวอร์ SQL ที่ทำงานบน VMware
อันที่จริง การรับเซิร์ฟเวอร์ที่ใช้งานจริงโดยไม่ทันตั้งตัวแล็ปท็อปนั้นเป็นเรื่องง่าย เรียกใช้ (ไม่ใช่ใน tempdb และไม่ได้อยู่ในฐานข้อมูลที่เปิดใช้งาน Delayed Durability) รหัส:
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
ใช้เวลา 5 วินาทีบนเดสก์ท็อปของฉันและ 28 วินาทีบนเซิร์ฟเวอร์ที่ใช้งานจริง เนื่องจาก SQL ต้องรอให้สิ้นสุดการเขียนบันทึกธุรกรรมจริง และเรากำลังทำธุรกรรมสั้นมากที่นี่ พูดอย่างคร่าว ๆ เราขับรถบรรทุกทรงพลังขนาดใหญ่เข้าไปในการจราจรในเมือง และเรากำลังดูว่าคนส่งพิซซ่าที่ขี่สกู๊ตเตอร์มีชื่อเสียงแซงหน้ามันได้อย่างไร - ปริมาณงานไม่สำคัญสำหรับที่นี่ เฉพาะเวลาแฝงเท่านั้นที่สำคัญ และไม่ใช่ที่จัดเก็บข้อมูลเครือข่ายเดียว ไม่ว่าจะมีราคาเป็นศูนย์กี่ตัวก็ตาม ก็จะมีประสิทธิภาพเหนือกว่า SSD ในเครื่องในแง่ของเวลาแฝง
(ในความคิดเห็นปรากฎว่าฉันโกหก - ฉันมีความทนทานล่าช้าทั้งสองแห่ง ปรากฎว่าไม่มีความทนทานล่าช้า:
เดสก์ท็อป - 39 วินาที 15K tr/วินาที 0.065ms /io ไปกลับ
PROD - 360 วินาที 1600 tr/วินาที 0.6ms
ฉันควรจะสังเกตว่ามันเร็วเกินไป)
อย่างไรก็ตาม ในกรณีนี้ เรากำลังจัดการกับเลขศูนย์เล็กน้อยของฟังก์ชัน Riemann zeta ด้วยตัวอย่างเล็กน้อย ในตัวอย่างที่นักพัฒนานำมาให้ฉันมันแตกต่างกัน ฉันเชื่อว่าพวกเขาพูดถูก และเริ่มล้างข้อมูลเฉพาะทั้งหมดที่เกี่ยวข้องกับตรรกะทางธุรกิจออกจากตัวอย่าง เมื่อถึงจุดหนึ่ง ฉันตระหนักว่าฉันสามารถทิ้งโค้ดของพวกเขาทั้งหมดและเขียนโค้ดของตัวเองได้ ซึ่งแสดงให้เห็นถึงปัญหาเดียวกัน ในการผลิตจะทำงานช้าลง 3-4 เท่า:
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
หากทุกอย่างเรียบร้อยดี การตรวจสอบความเรียบง่ายของตัวเลขจะใช้เวลา 6-7-8 วินาที เหตุการณ์นี้เกิดขึ้นกับเซิร์ฟเวอร์จำนวนหนึ่ง แต่สำหรับบางคน การตรวจสอบใช้เวลา 25-40 วินาที ที่น่าสนใจคือไม่มีเซิร์ฟเวอร์ใดที่การประมวลผลจะใช้เวลา 14 วินาที - โค้ดทำงานได้เร็วหรือช้ามาก นั่นคือปัญหาคือ สมมุติว่าขาวดำ
ฉันทำอะไรลงไป? เข้าสู่เมตริก VMware ทุกอย่างเรียบร้อยดี - มีทรัพยากรมากมาย เวลาพร้อม = 0 มีทุกอย่างเพียงพอ ระหว่างการทดสอบทั้งบนเซิร์ฟเวอร์ที่เร็วและช้า CPU = 100 บน vCPU เดียว ฉันทำการทดสอบเพื่อคำนวณจำนวน Pi - การทดสอบแสดงผลเหมือนกันบนเซิร์ฟเวอร์ใดๆ กลิ่นมนต์ดำเริ่มแรงขึ้นเรื่อยๆ
หลังจากออกจากฟาร์ม DEV ฉันก็เริ่มเล่นกับเซิร์ฟเวอร์ ปรากฎว่า vMotion จากโฮสต์หนึ่งไปอีกโฮสต์หนึ่งสามารถ "รักษา" เซิร์ฟเวอร์ได้ แต่ก็สามารถเปลี่ยนเซิร์ฟเวอร์ที่ "เร็ว" ให้กลายเป็นเซิร์ฟเวอร์ที่ "ช้า" ได้เช่นกัน ดูเหมือนว่าจะเป็น - โฮสต์บางคนมีปัญหา ... แต่ ... ไม่ เครื่องเสมือนบางเครื่องทำงานช้าลงบนโฮสต์ เช่น A แต่ทำงานได้อย่างรวดเร็วบนโฮสต์ B ส่วนเครื่องเสมือนอีกเครื่องกลับทำงานเร็วบน A และทำงานช้าลงใน B! รถทั้ง "เร็ว" และ "ช้า" มักจะหมุนเข้าหาเจ้าบ้าน!
ตั้งแต่นั้นเป็นต้นมา มีกลิ่นกำมะถันที่เด่นชัดในอากาศ ท้ายที่สุดแล้วปัญหาไม่สามารถระบุได้ว่ามาจากเครื่องเสมือนใด ๆ (เช่นโปรแกรมแก้ไข windows) - ท้ายที่สุดมันกลายเป็นเครื่องที่ "เร็ว" ด้วย vMotion แต่ปัญหาไม่สามารถนำมาประกอบกับโฮสต์ได้ - ท้ายที่สุดอาจมีทั้งเครื่องที่ "เร็ว" และ "ช้า" มันยังไม่เกี่ยวข้องกับการโหลด - ฉันจัดการเพื่อให้ได้เครื่องที่ "ช้า" บนโฮสต์ซึ่งไม่มีอะไรเลยนอกจากนั้น
ด้วยความสิ้นหวัง ฉันเรียกใช้ Process Explorer ของ Sysinternals และดูที่สแต็ก SQL สำหรับเครื่องที่ทำงานช้า สายตาของฉันก็สะดุดทันที:
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
… ข้ามไป
sqldk.dll!SystemThread::MakeMiniSOSTthread+0xa54
KERNEL32.DLL!BaseThreadInitThunk+0x14
ntdll.dll RtlUserThreadStart + 0x21!
มันเป็นอะไรบางอย่างแล้ว โปรแกรมถูกเขียนขึ้น:
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);
}
}
}
}
โปรแกรมนี้แสดงการชะลอตัวที่เด่นชัดยิ่งขึ้น - บนเครื่อง "เร็ว" จะแสดง 16-18 ล้านรอบต่อวินาทีในขณะที่เครื่องช้า - หนึ่งล้านครึ่งหรือแม้กระทั่ง 700 นั่นคือความแตกต่างคือ 10-20 เท่า (!!!) นี่เป็นชัยชนะเล็ก ๆ น้อย ๆ อยู่แล้ว ไม่ว่าในกรณีใด ไม่มีการติดขัดระหว่างฝ่ายสนับสนุนของ Microsoft และ VMware เพื่อให้พวกเขาสลับลูกศรกัน
จากนั้นความคืบหน้าก็หยุดลง - วันหยุด สิ่งสำคัญ ฮิสทีเรียจากไวรัส และภาระงานที่เพิ่มขึ้นอย่างรวดเร็ว ฉันมักจะพูดถึงปัญหามหัศจรรย์กับเพื่อนร่วมงาน แต่บางครั้งดูเหมือนว่าพวกเขาจะไม่เชื่อฉันด้วยซ้ำ คำพูดที่ว่า VMware ทำให้โค้ดช้าลง 10-20 เท่านั้นเป็นสิ่งที่น่ากลัวเกินไป
ฉันพยายามค้นหาตัวเองว่าอะไรที่ทำให้ช้าลง บางครั้งฉันคิดว่าฉันพบวิธีแก้ปัญหาแล้ว - การเปิดและปิด Hot plugs การเปลี่ยนจำนวนหน่วยความจำหรือจำนวนโปรเซสเซอร์มักทำให้เครื่องกลายเป็น "เครื่องเร็ว" แต่ไม่ตลอดไป แต่สิ่งที่กลายเป็นความจริงก็คือการออกไปเคาะล้อก็เพียงพอแล้วนั่นคือการเปลี่ยนแปลง ใด พารามิเตอร์เครื่องเสมือน
ในที่สุดเพื่อนร่วมงานชาวอเมริกันของฉันก็พบสาเหตุที่แท้จริง
เจ้าภาพความถี่ต่างกัน!
- ตามกฎแล้วมันไม่น่ากลัว แต่: เมื่อย้ายจากโฮสต์ 'ดั้งเดิม' ไปยังโฮสต์ที่มีความถี่ 'ต่างกัน' VMware จะต้องปรับผลลัพธ์ GetTimePrecise
- ตามกฎแล้ว นี่ไม่ใช่ปัญหา เว้นแต่จะมีแอปพลิเคชันที่ร้องขอเวลาที่แน่นอนหลายล้านครั้งต่อวินาที เช่น SQL Server
- แต่ก็ไม่น่ากลัวเช่นกันเนื่องจากเซิร์ฟเวอร์ SQL ไม่ได้ทำเช่นนี้เสมอไป (ดูบทสรุป)
แต่มีบางกรณีที่คราดนี้เจ็บ และใช่ โดยการเคาะวงล้อ (โดยการเปลี่ยนแปลงบางอย่างในการตั้งค่า VM) ฉันบังคับให้ VMware 'คำนวณ' การกำหนดค่าใหม่ และความถี่ของโฮสต์ปัจจุบันกลายเป็นความถี่ 'ดั้งเดิม' ของเครื่อง
การตัดสิน
เมื่อคุณปิดใช้งานการจำลองเสมือนของ TSC การอ่าน TSC จากภายในเครื่องเสมือนจะส่งคืนค่า TSC ของเครื่องจริง และการเขียน TSC จากภายในเครื่องเสมือนจะไม่มีผลใดๆ การโอนย้ายเครื่องเสมือนไปยังโฮสต์อื่น ดำเนินการต่อจากสถานะหยุดชั่วคราว หรือการย้อนกลับเป็นสแน็ปช็อต ทำให้ TSC ข้ามไปอย่างไม่ต่อเนื่อง ระบบปฏิบัติการเกสต์บางระบบไม่สามารถบู๊ตได้ หรือแสดงปัญหาอื่นๆ เกี่ยวกับการบอกเวลา เมื่อปิดใช้งานการจำลองเสมือน TSC ในอดีต คุณลักษณะนี้ได้รับการแนะนำในบางครั้งเพื่อปรับปรุงประสิทธิภาพของแอปพลิเคชันที่อ่าน TSC บ่อยๆแต่ประสิทธิภาพของ TSC เสมือนได้รับการปรับปรุงอย่างมากในผลิตภัณฑ์ปัจจุบัน คุณลักษณะนี้ยังได้รับการแนะนำให้ใช้เมื่อทำการวัดที่ต้องการแหล่งที่มาของเวลาจริงที่แม่นยำในเครื่องเสมือน
ในระยะสั้น คุณต้องเพิ่มพารามิเตอร์
monitor_control.virtual_rdtsc = FALSE
ข้อสรุป
คุณอาจมีคำถาม: ทำไม SQL ถึงเรียก GetTimePrecise บ่อยนัก
ฉันไม่มีแหล่งที่มาของเซิร์ฟเวอร์ SQL แต่ตรรกะบอกว่าสิ่งนี้ SQL เกือบจะเป็นระบบปฏิบัติการที่มีการทำงานร่วมกันพร้อมกัน ซึ่งแต่ละเธรดต้อง "หลีกทาง" เป็นครั้งคราว ที่ไหนดีที่จะทำ? ในกรณีที่มีความคาดหวังตามธรรมชาติ - ล็อคหรือ IO โอเค แต่ถ้าเรากำลังหมุนรอบการคำนวณล่ะ จากนั้นสถานที่ที่ชัดเจนและเกือบจะเป็นที่เดียวคือล่าม (นี่ไม่ใช่ล่ามเสียทีเดียว) หลังจากการดำเนินการของผู้ดำเนินการรายต่อไป
ตามกฎแล้ว SQL Server จะไม่ใช้สำหรับการประมวลผลแบบ Pure Computing และนี่ไม่ใช่ปัญหา แต่การหมุนเวียนด้วยการทำงานกับตารางชั่วคราวทุกประเภท (ซึ่งจะถูกแคชทันที) ทำให้โค้ดกลายเป็นลำดับของคำสั่งที่ดำเนินการอย่างรวดเร็ว
อย่างไรก็ตาม หากฟังก์ชันถูกรวมไว้ใน NATIVELY COMPILED มันจะหยุดร้องขอเวลาและความเร็วของมันจะเพิ่มขึ้น 10 เท่า แต่การทำงานหลายอย่างแบบร่วมมือกันล่ะ แต่สำหรับโค้ดที่คอมไพล์แล้ว ฉันต้องทำ PREEMPTIVE MULTITASKING ใน SQL
ที่มา: will.com