เหตุใดจึงแนะนำให้เก็บ BLOB ในตาราง SQL Server แยกต่างหาก


28

คำตอบ SO up-upvoted นี้แนะนำให้วางรูปภาพในตารางแยกกันแม้ว่าจะมีความสัมพันธ์แบบ 1: 1 กับตารางอื่นเท่านั้น:

หากคุณตัดสินใจที่จะวางรูปภาพของคุณลงในตาราง SQL Server ฉันขอแนะนำอย่างยิ่งให้ใช้ตารางแยกต่างหากสำหรับการจัดเก็บรูปภาพเหล่านั้น - อย่าเก็บรูปถ่ายของพนักงานในตารางพนักงาน - เก็บไว้ในตารางแยกต่างหาก ด้วยวิธีนี้ตารางพนักงานสามารถอยู่ได้อย่างคล่องตัวและมีความหมายและมีประสิทธิภาพมากโดยสมมติว่าคุณไม่จำเป็นต้องเลือกรูปถ่ายของพนักงานเช่นกันซึ่งเป็นส่วนหนึ่งของคำสั่งของคุณ

ทำไม? ฉันรู้สึกว่าSQL Server จะเก็บตัวชี้ไปที่โครงสร้างข้อมูล BLOB เฉพาะบางตัวในตารางเท่านั้นดังนั้นทำไมต้องสร้างเลเยอร์ทางอ้อมอีกด้านหนึ่งด้วยตนเอง มันปรับปรุงประสิทธิภาพอย่างมีนัยสำคัญจริงๆเหรอ? ถ้าใช่ทำไม

คำตอบ:


15

ในขณะที่ฉันไม่เห็นว่า BLOB ควรอยู่ในตารางอื่น แต่ไม่ควรอยู่ในฐานข้อมูลเลย เก็บตัวชี้ไปยังตำแหน่งที่ไฟล์ใช้งานบนดิสก์จากนั้นรับจากฐานข้อมูล ...

ปัญหาหลักที่ทำให้เกิด (สำหรับฉัน) คือการทำดัชนี การใช้ XML กับแผนแบบสอบถามเนื่องจากทุกคนมีส่วนร่วมกันทำตาราง:

SELECT TOP 1000
ID = IDENTITY(INT,1,1),
deq.query_plan
INTO dbo.index_test
FROM sys.dm_exec_cached_plans AS dec
CROSS APPLY sys.dm_exec_query_plan(dec.plan_handle) AS deq

ALTER TABLE dbo.index_test ADD CONSTRAINT pk_id PRIMARY KEY CLUSTERED (ID)

มีเพียง 1,000 แถว แต่ตรวจสอบขนาด ...

sp_BlitzIndex @DatabaseName = 'StackOverflow', @SchemaName = 'dbo', @TableName = 'index_test'

มากกว่า 40 MB เพียง 1,000 แถวเท่านั้น สมมติว่าคุณเพิ่ม 40 MB ทุก ๆ 1,000 แถวที่สามารถทำให้สวยน่าเกลียดได้อย่างรวดเร็ว จะเกิดอะไรขึ้นเมื่อคุณไปถึง 1 ล้านแถว นั่นคือข้อมูลประมาณ 1 TB

ถั่ว

แบบสอบถามใด ๆ ที่จำเป็นต้องใช้ดัชนีคลัสเตอร์ของคุณตอนนี้จำเป็นต้องอ่านข้อมูล BLOB ทั้งหมดนั้นลงในการชี้แจงหน่วยความจำ:เมื่อมีการอ้างอิงคอลัมน์ข้อมูล BLOB

คุณนึกถึงวิธีที่ดีกว่าในการใช้หน่วยความจำ SQL Server มากกว่าการจัดเก็บ BLOB หรือไม่ เพราะฉันสามารถ

การขยายไปยังดัชนีที่ไม่ได้เป็นคลัสเตอร์:

CREATE INDEX ix_noblob ON dbo.index_test (ID)

CREATE INDEX ix_returnoftheblob ON dbo.index_test (ID) INCLUDE (query_plan)

คุณสามารถออกแบบดัชนี nonclustered ของคุณเพื่อหลีกเลี่ยงคอลัมน์ BLOB เป็นส่วนใหญ่ดังนั้นการสืบค้นปกติสามารถหลีกเลี่ยงดัชนีคลัสเตอร์ แต่ทันทีที่คุณต้องการคอลัมน์ BLOB คุณจำเป็นต้องมีดัชนีคลัสเตอร์

หากคุณเพิ่มเป็นINCLUDEDคอลัมน์ในดัชนี nonclustered เพื่อหลีกเลี่ยงสถานการณ์การค้นหาคีย์คุณท้ายด้วยดัชนี nonclustered ยักษ์:ป้อนคำอธิบายรูปภาพที่นี่

ปัญหาอื่น ๆ ที่พวกเขาทำให้:

  • ถ้าใครเรียกใช้SELECT *แบบสอบถามพวกเขาได้รับข้อมูล BLOB ทั้งหมด
  • พวกเขาใช้พื้นที่ในการสำรองข้อมูลและเรียกคืนทำให้ช้าลง
  • พวกเขาช้าลงDBCC CHECKDBเพราะฉันรู้ว่าคุณกำลังตรวจสอบการทุจริตใช่ไหม
  • และถ้าคุณทำการบำรุงรักษาดัชนีใด ๆ พวกเขาก็จะชะลอตัวลงเช่นกัน

หวังว่านี่จะช่วยได้!


7
เนื่องจากผู้ใช้มักจะพิมพ์ SELECT *
Brent Ozar

ฉันคิดว่าข้อเสียที่คุณพูดถึงเป็นส่วนหนึ่งของสาเหตุที่เขาแนะนำให้วางรูปภาพในตารางแยกต่างหาก หากฉันใช้งานรายงานต่าง ๆ เกี่ยวกับผู้ใช้ฉันไม่ต้องการไฟล์รูปภาพของพวกเขา หากฉันกำลังโหลดหน้าโปรไฟล์ของผู้ใช้คนเดียวนั่นคือเมื่อฉันเข้าร่วมในตารางหยดใช่มั้ย ฉันขาดอะไรบางอย่างที่นี่ (เช่นข้อเสียของคุณยังคงใช้จริงแม้ในสถานการณ์ที่ฉันอธิบายไว้นี้)
BVernon

11

รูปภาพเหล่านี้มีขนาดใหญ่เพียงใดและคุณคาดหวังว่าจะมีกี่รูป ในขณะที่ฉันเห็นด้วยกับ@sp_BlitzErikเป็นส่วนใหญ่ฉันคิดว่ามีบางสถานการณ์ที่สามารถทำได้และจะช่วยให้มีภาพที่ชัดเจนยิ่งขึ้นเกี่ยวกับสิ่งที่ขอมาที่นี่

ตัวเลือกบางอย่างที่ควรพิจารณาว่าช่วยลดผลกระทบด้านลบส่วนใหญ่ที่ Erik กล่าวไว้คือ:

  • FILESTREAM (เริ่มต้นใน SQL Server 2008)
  • FileTables (เริ่มใน SQL Server 2012)

ตัวเลือกทั้งสองนี้ได้รับการออกแบบให้เป็นสื่อกลางระหว่างการจัดเก็บ BLOB ทั้งใน SQL Server หรือนอกอย่างเต็มที่ (ยกเว้นสำหรับสตริง colun เพื่อรักษาเส้นทาง) พวกเขาอนุญาตให้ BLOBs เป็นส่วนหนึ่งของตัวแบบข้อมูลและมีส่วนร่วมในธุรกรรมในขณะที่ไม่เปลืองเนื้อที่ในบัฟเฟอร์พูล (เช่นหน่วยความจำ) ข้อมูลหยดยังคงรวมอยู่ในการสำรองข้อมูลซึ่งไม่ทำให้พวกเขาใช้พื้นที่มากขึ้นและใช้เวลานานในการสำรองข้อมูลและเพื่อเรียกคืน อย่างไรก็ตามฉันมีเวลายากที่จะเห็นว่านี่เป็นเชิงลบจริงเพราะถ้ามันเป็นส่วนหนึ่งของแอปมันต้องสำรองไว้อย่างใดและมีเพียงคอลัมน์สตริงที่มีเส้นทางถูกตัดการเชื่อมต่ออย่างสมบูรณ์และอนุญาตให้ไฟล์ BLOBs ได้รับ ลบโดยไม่มีข้อบ่งชี้ว่าใน DB (เช่นตัวชี้ / ไฟล์ที่ไม่ถูกต้อง) นอกจากนี้ยังช่วยให้ไฟล์ "ถูกลบ" ในฐานข้อมูล แต่ยังคงอยู่ในระบบไฟล์ซึ่งจะต้องมีการทำความสะอาดในที่สุด (เช่นปวดหัว) แต่ถ้าไฟล์มีขนาดใหญ่มากก็อาจเป็นการดีที่สุดที่จะออกนอก SQL Server ทั้งหมดยกเว้นคอลัมน์พา ธ

ที่ช่วยในคำถาม "ภายในหรือภายนอก" แต่ไม่ได้สัมผัสกับคำถามเดียวกับตารางหลายคำถาม ฉันสามารถพูดได้ว่านอกเหนือจากคำถามเฉพาะนี้มีกรณีที่ถูกต้องอย่างแน่นอนสำหรับการแบ่งตารางออกเป็นกลุ่มของคอลัมน์ตามรูปแบบการใช้งาน บ่อยครั้งเมื่อมี 50 หรือมากกว่าคอลัมน์มีบางคนที่เข้าถึงบ่อยและบางคนที่ไม่ได้ บางคอลัมน์ถูกเขียนเป็นบ่อยในขณะที่บางส่วนจะถูกอ่าน การแยกการเข้าถึงบ่อยครั้งและคอลัมน์ที่มีการเข้าถึงไม่บ่อยออกเป็นหลายตารางที่มีความสัมพันธ์แบบ 1: 1 ค่อนข้างบ่อยครั้งจะมีประโยชน์เพราะเหตุใดจึงเสียพื้นที่ในบัฟเฟอร์พูลสำหรับข้อมูลที่คุณอาจไม่ได้ใช้VARBINARY(MAX)คอลัมน์เป็นปัญหา) หรือไม่ คุณยังเพิ่มประสิทธิภาพของคอลัมน์การเข้าถึงบ่อย ๆ โดยการลดขนาดแถวและทำให้แถวที่พอดีมากขึ้นบนหน้าข้อมูลทำให้การอ่าน (ทั้งแบบฟิสิคัลและโลจิคัล) มีประสิทธิภาพมากขึ้น แน่นอนคุณยังแนะนำความไร้ประสิทธิภาพบางอย่างโดยจำเป็นต้องทำซ้ำ PK และตอนนี้บางครั้งคุณจำเป็นต้องเข้าร่วมสองตารางซึ่งซับซ้อนด้วย (แม้เพียงเล็กน้อย) แบบสอบถามบางอย่าง

ดังนั้นจึงมีหลายวิธีที่คุณสามารถทำได้และสิ่งที่ดีที่สุดขึ้นอยู่กับสภาพแวดล้อมของคุณและสิ่งที่คุณพยายามจะทำ


ฉันรู้สึกว่า SQL Server จะเก็บตัวชี้ไปยังโครงสร้างข้อมูล BLOB เฉพาะบางตัวในตาราง

ไม่ง่ายนัก คุณสามารถหาข้อมูลที่ดีได้ที่นี่อะไรคือขนาดของ LOB Pointer สำหรับ (MAX) ประเภทเช่น Varchar, Varbinary, Etc? แต่พื้นฐานคือ:

  • TEXT, NTEXTและIMAGEประเภทข้อมูล (โดยค่าเริ่มต้น): ตัวชี้ 16 ไบต์
  • VARCHAR(MAX), NVARCHAR(MAX), VARBINARY(MAX)(โดยเริ่มต้น):
    • หากข้อมูลสามารถอยู่ในแถวแล้วมันจะถูกวางไว้ที่นั่น
    • หากข้อมูลมีค่าน้อยกว่าประมาณ 40,000 ไบต์ (บล็อกโพสต์ที่เชื่อมโยงแสดง 40,000 เป็นขีด จำกัด บน แต่การทดสอบของฉันแสดงค่าที่สูงกว่าเล็กน้อย) และถ้ามีพื้นที่ว่างในแถวสำหรับโครงสร้างนี้จะมีลิงก์ระหว่าง 1 ถึง 5 โดยตรงไปยังหน้า LOB เริ่มต้นที่ 24 ไบต์สำหรับลิงก์แรกไปที่ 8000 ไบต์แรกและเพิ่มขึ้น 12 ไบต์ต่อการเชื่อมโยงเพิ่มเติมสำหรับแต่ละชุดเพิ่มเติมที่ 8000 ไบต์สูงสุด 72 ไบต์สูงสุด
    • หากข้อมูลมีค่ามากกว่า 40,000 ไบต์หรือมีที่ว่างไม่เพียงพอในการจัดเก็บลิงก์โดยตรงจำนวนที่เหมาะสม (เช่นเหลือเพียง 40 ไบต์บนแถวและค่า 20,000 ไบต์ต้องการ 3 ลิงก์ซึ่งเป็น 24 ไบต์สำหรับ 12 ตัวแรกและ 12 ลิงก์เพิ่มเติม 48 ไบต์ รวมพื้นที่ในแถวที่ต้องการ) จากนั้นจะมีตัวชี้ 24 ไบต์ไปยังหน้าต้นไม้ข้อความซึ่งมีลิงก์ไปยังหน้า LOB)

7

หากข้อมูลต้องถูกเก็บไว้ใน SQL Server ไม่ว่าด้วยเหตุผลใดฉันสามารถนึกถึงประโยชน์เล็กน้อยในการจัดเก็บไว้ในตารางแยกต่างหาก บางคนมีความเชื่อมากกว่าคนอื่น

  1. การวางข้อมูลในตารางแยกต่างหากหมายความว่าคุณสามารถเก็บข้อมูลไว้ในฐานข้อมูลแยกต่างหาก สิ่งนี้มีข้อดีสำหรับการบำรุงรักษาตามกำหนดเวลา ตัวอย่างเช่นคุณสามารถเรียกใช้DBCC CHECKDBเฉพาะในฐานข้อมูลที่มีข้อมูล BLOB

  2. หากคุณไม่ใส่มากกว่า 8000 ไบต์ลงใน BLOB เสมอไปจะสามารถเก็บไว้ในแถวสำหรับแถวบางแถวได้ คุณอาจไม่ต้องการเพราะจะทำให้การสืบค้นที่ใช้ข้อมูลโดยใช้ดัชนีคลัสเตอร์ช้าลงแม้ว่าแบบสอบถามจะไม่ต้องการคอลัมน์ก็ตาม การใส่ข้อมูลในตารางแยกกันจะช่วยลดความเสี่ยงนี้ได้

  3. เมื่อเก็บไว้นอกแถว SQL Server จะใช้ตัวชี้มากถึง 24 ไบต์เพื่อชี้ไปที่หน้าใหม่ ที่ใช้พื้นที่และ จำกัด จำนวนคอลัมน์ BLOB ทั้งหมดที่คุณสามารถเพิ่มลงในตารางเดียว ดูคำตอบของ srutzky สำหรับรายละเอียดเพิ่มเติม

  4. ไม่สามารถกำหนดดัชนี columnstore ของคลัสเตอร์บนตารางที่มีคอลัมน์ BLOB ข้อ จำกัด นี้ถูกลบออกจะถูกลบใน SQL Server 2017

  5. หากในที่สุดคุณตัดสินใจว่าควรย้ายข้อมูลภายนอก SQL Server อาจเป็นการง่ายกว่าที่จะทำการเปลี่ยนแปลงหากข้อมูลอยู่ในตารางแยกต่างหาก


1
จุดที่ดีบางอย่างที่นี่ (+1) แต่เพื่อให้ชัดเจนเกี่ยวกับ # 3 (อีก: 24 ไบต์ตัวชี้สำหรับข้อมูลแบบแถว) ที่ไม่ถูกต้องเสมอ ฉันอธิบาย (สั้น ๆ ) ที่ด้านล่างของคำตอบของฉันว่าประเภทข้อมูลขนาดของค่าและจำนวนพื้นที่ว่างในแถวกำหนดขนาดของตัวชี้อย่างไร
โซโลมอน Rutzky
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.