ผู้เช่ารายแรก:นี่เป็นสิ่งที่ไม่เลือกเลย อาจมีการเปลี่ยนแปลงเล็กน้อยใน 1 ล้านแถวถ้าคุณมีผู้เช่า 100 รายเท่านั้น แต่สถิติของแบบสอบถามเหล่านี้มีความแม่นยำมากขึ้นเนื่องจาก SQL Server จะทราบว่าแบบสอบถามสำหรับผู้เช่า A จะดึงแถว 500,000 แถวกลับมา แต่แบบสอบถามเดียวกันสำหรับผู้เช่า B นั้นมีเพียง 50 แถว นี่คือที่ที่จุดปวดหลักคือ วิธีนี้เพิ่มโอกาสในการมีปัญหาการดมพารามิเตอร์อย่างมากซึ่งการเรียกใช้กระบวนงานที่เก็บไว้ครั้งแรกสำหรับผู้เช่า A และดำเนินการอย่างเหมาะสมโดยใช้ Query Optimizer ที่ดูสถิติเหล่านั้นและรู้ว่าจำเป็นต้องมีแถว 500k ที่มีประสิทธิภาพ แต่เมื่อผู้เช่า B ที่มีเพียง 50 แถวทำงานแผนปฏิบัติการนั้นไม่เหมาะสมอีกต่อไปและในความเป็นจริงแล้วมันไม่เหมาะสมเลยทีเดียว และเนื่องจากข้อมูลไม่ได้ถูกแทรกตามลำดับของฟิลด์นำ
อย่างไรก็ตามสำหรับ TenantID แรกที่เรียกใช้กระบวนงานที่เก็บไว้ประสิทธิภาพควรดีกว่าในแนวทางอื่นเนื่องจากข้อมูล (อย่างน้อยหลังจากทำการบำรุงรักษาดัชนี) จะถูกจัดระเบียบทางร่างกายและทางตรรกะเพื่อให้หน้าข้อมูลน้อยลงเพื่อตอบสนอง คำสั่ง ซึ่งหมายถึง I / O ทางกายภาพที่น้อยลงการอ่านแบบลอจิคัลที่น้อยลงความขัดแย้งระหว่างผู้เช่าสำหรับหน้าข้อมูลเดียวกันน้อยกว่าพื้นที่ว่างที่เกิดขึ้นในบัฟเฟอร์ Buffer น้อยลง (เช่นปรับปรุงอายุการใช้งานของหน้า) เป็นต้น
มีสองค่าใช้จ่ายหลักในการปรับปรุงประสิทธิภาพนี้ ครั้งแรกนั้นไม่ใช่เรื่องยากนัก: คุณต้องทำการบำรุงรักษาดัชนีเป็นประจำเพื่อตอบโต้การแตกกระจายของข้อมูล ประการที่สองคือความสนุกน้อยลงเล็กน้อย
ในการรับมือกับปัญหาการสูดดมพารามิเตอร์ที่เพิ่มขึ้นคุณต้องแยกแผนการดำเนินการระหว่างผู้เช่า วิธีการง่าย ๆ คือการใช้WITH RECOMPILE
กับ procs หรือOPTION (RECOMPILE)
คำใบ้แบบสอบถาม แต่นั่นคือผลการดำเนินงานที่เป็นที่นิยมซึ่งสามารถกำจัดกำไรทั้งหมดที่เกิดขึ้นได้จากการใส่TenantID
ครั้งแรก วิธีการที่ผมพบว่าการทำงานที่ดีที่สุดคือการใช้แปร SQL sp_executesql
แบบไดนามิกผ่าน เหตุผลที่ต้องการ Dynamic SQL คือการอนุญาตให้เชื่อมโยง TenantID เข้ากับข้อความของแบบสอบถามในขณะที่เพรดิเคตอื่น ๆ ทั้งหมดที่ปกติจะเป็นพารามิเตอร์จะยังคงเป็นพารามิเตอร์ ตัวอย่างเช่นหากคุณกำลังมองหาคำสั่งซื้อเฉพาะคุณจะทำสิ่งที่ชอบ:
DECLARE @GetOrderSQL NVARCHAR(MAX);
SET @GetOrderSQL = N'
SELECT ord.field1, ord.field2, etc.
FROM dbo.Orders ord
WHERE ord.TenantID = ' + CONVERT(NVARCHAR(10), @TenantID) + N'
AND ord.OrderID = @OrderID_dyn;
';
EXEC sp_executesql
@GetOrderSQL,
N'@OrderID_dyn INT',
@OrderID_dyn = @OrderID;
ผลที่ได้นี้คือการสร้างแผนคิวรีที่สามารถใช้ซ้ำได้สำหรับ TenantID นั้นที่จะจับคู่กับปริมาณข้อมูลของผู้เช่ารายนั้น หากผู้เช่ารายเดียวกันนั้นดำเนินการตามขั้นตอนที่เก็บไว้อีกครั้งผู้ใช้ราย@OrderID
นั้นจะใช้แผนคิวรีแคชนั้นซ้ำ ผู้เช่ารายอื่นที่ใช้ขั้นตอนการจัดเก็บเดียวกันนั้นจะสร้างข้อความค้นหาที่แตกต่างกันเฉพาะในมูลค่าของ TenantID แต่ความแตกต่างใด ๆในข้อความค้นหานั้นเพียงพอที่จะสร้างแผนที่แตกต่างกัน และแผนที่สร้างขึ้นสำหรับผู้เช่า B จะไม่เพียง แต่ตรงกับปริมาณข้อมูลสำหรับผู้เช่า B แต่จะสามารถนำมาใช้ซ้ำสำหรับผู้เช่า B สำหรับค่าที่แตกต่างกันของ@OrderID
(เนื่องจากภาคแสดงยังคงเป็นพารามิเตอร์)
ข้อเสียของวิธีนี้คือ:
- มันเป็นงานเล็ก ๆ น้อย ๆ มากกว่าเพียงแค่พิมพ์ในแบบสอบถามง่าย ๆ (แต่ไม่ใช่แบบสอบถามทั้งหมดจะต้องเป็น SQL แบบไดนามิกเพียงแค่คนที่จบลงด้วยปัญหาการดมกลิ่นพารามิเตอร์)
- ขึ้นอยู่กับจำนวนผู้เช่าที่อยู่บนระบบมันจะเพิ่มขนาดของแคชแผนเนื่องจากขณะนี้แต่ละแบบสอบถามต้องการ 1 แผนต่อ TenantID ที่กำลังเรียกใช้ นี่อาจไม่ใช่ปัญหา แต่อย่างน้อยสิ่งที่ต้องระวัง
Dynamic SQL จะทำลายห่วงโซ่ความเป็นเจ้าของซึ่งหมายความว่าไม่สามารถสันนิษฐานได้ว่าการเข้าถึงแบบอ่าน / เขียนไปยังตารางโดยได้EXECUTE
รับอนุญาตใน Stored Procedure การแก้ไขที่ง่าย แต่ปลอดภัยน้อยนั้นเป็นเพียงการให้ผู้ใช้เข้าถึงตารางโดยตรง มันไม่เหมาะอย่างแน่นอน แต่โดยปกติแล้วจะเป็นการแลกเปลี่ยนเพื่อความรวดเร็วและง่ายดาย แนวทางที่ปลอดภัยยิ่งขึ้นคือการใช้ความปลอดภัยที่ใช้ใบรับรอง ความหมายสร้างใบรับรองแล้วสร้างผู้ใช้จากที่รับรองให้สิทธิ์ที่ผู้ใช้สิทธิ์ที่ต้องการ (ผู้ใช้รับรองหรือเข้าสู่ระบบไม่สามารถเชื่อมต่อกับ SQL Server ของตัวเอง) แล้วเข้าสู่ระบบวิธีการจัดเก็บที่ใช้แบบไดนามิก SQL กับที่ รับรองเดียวกันผ่านทางเพิ่มลายเซ็น
สำหรับข้อมูลเพิ่มเติมเกี่ยวกับโมดูลการเซ็นชื่อและใบรับรองโปรดดูที่: ModuleSigning.Info