ทำไม InnoDB ถึงไม่เก็บจำนวนแถว?


19

ทุกคนรู้ว่าในตารางที่ใช้ InnoDB เป็นเอ็นจินแบบสอบถามSELECT COUNT(*) FROM mytableจะไม่แม่นยำและช้ามากโดยเฉพาะเมื่อตารางมีขนาดใหญ่ขึ้นและมีการแทรก / ลบแถวอย่างต่อเนื่องในขณะที่ดำเนินการค้นหา

ดังที่ฉันเข้าใจแล้ว InnoDB ไม่ได้จัดเก็บจำนวนแถวในตัวแปรภายในซึ่งเป็นสาเหตุของปัญหานี้

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

PS: ฉันไม่ใช่ผู้เชี่ยวชาญเกี่ยวกับ DBs ฉันเป็นแค่คนที่มี MySQL เป็นงานอดิเรกง่ายๆ ดังนั้นถ้าฉันถามอะไรที่โง่ ๆ


6
ช้าใช่ ไม่แน่นอนไม่มี มันช้าเพราะมันให้ผลลัพธ์ที่แน่นอน เมื่อคุณมีตาราง 200M แถวและธุรกรรมอื่น ๆ อีกมากมายที่แทรก / ลบในตารางเดียวกันอาจมีหลายแถวต่อวินาทีคำถามอื่นคือ "คุณต้องการหมายเลขที่แน่นอนหรือไม่"
ypercubeᵀᴹ

@ypercube ฉันรู้ว่าฉันเห็นสองสามครั้งใน phpmyadmin บางค่านับแถวที่ปิดมาก นอกจากนี้ยังมีความคิดเห็นที่บอกว่าบางสิ่งเช่น "อาจไม่ถูกต้อง"
Radu Murzea

1
ผู้ใช้ @RaduMurzea phpMyAdmin เป็นวิธีทางเลือกในการคำนวณการนับตารางสำหรับตาราง InnoDB ด้วยเหตุผลด้านความเร็วที่คุณรู้ นี่คือสิ่งที่คุณพูดถึงความไม่ถูกต้อง SELECT COUNT(*) FROM ...ข้อความค้นหาที่แท้จริงนั้นแม่นยำ หากคุณต้องการ phpMyAdmin สามารถกำหนดค่าให้ใช้การนับแถวที่แน่นอนโดยใช้ความเร็ว ข้อมูลเพิ่มเติม: stackoverflow.com/questions/11926259/…
DOOManiac

คำตอบ:


9

ฉันเห็นด้วยกับ @RemusRusanu (+1 สำหรับคำตอบของเขา)

SELECT COUNT(*) FROM mydb.mytableใน InnoDB ควรทำตัวเหมือนเครื่องมือเก็บข้อมูลของทรานแซคชัน เปรียบเทียบกับ MyISAM

MyISAM

ถ้าmydb.mytableเป็นตาราง MyISAM ที่เปิดตัวเป็นเช่นเดียวกับการทำงานSELECT COUNT(*) FROM mydb.mytable; SELECT table_rows FROM information_schema.table WHERE table_schema = 'mydb' AND table_name = 'mytable';สิ่งนี้ทริกเกอร์การค้นหาอย่างรวดเร็วของการนับแถวในส่วนหัวของตาราง MyISAM

InnoDB

หากmydb.mytableเป็นตาราง InnoDB คุณจะได้รับสิ่งที่เกิดขึ้น คุณมี MVCC ดำเนินการควบคุมสิ่งต่อไปนี้:

  • ib_logfile0 / ib_logfile1 (ทำซ้ำบันทึก)
  • ibdata1
    • เลิกทำการบันทึก
    • rollbacks
    • การเปลี่ยนแปลงพจนานุกรมข้อมูล
  • การจัดการบัฟเฟอร์ของบัฟเฟอร์
  • การแยกธุรกรรม (4 ประเภท)
    • อ่านซ้ำ
    • อ่านความมุ่งมั่น
    • อ่านปราศจากข้อผูกมัด
    • serializable

การขอ InnoDB สำหรับการนับตารางต้องมีการนำทางผ่านสิ่งที่เป็นลางร้ายเหล่านี้ ในความเป็นจริงไม่มีใครรู้จริง ๆ ว่าการSELECT COUNT(*) from mydb.mytableนับอ่านซ้ำเท่านั้นหรือรวมถึงการอ่านที่ได้กระทำและผู้ที่ปราศจากข้อผูกมัด

คุณอาจจะพยายามที่จะรักษาเสถียรภาพของสิ่งเล็ก ๆ น้อย ๆ ด้วยการทำให้innodb_stats_on_metadata

ตามเอกสาร MySQL ในinnodb_stats_on_meta_data

เมื่อเปิดใช้งานตัวแปรนี้ (ซึ่งเป็นค่าเริ่มต้นเหมือนก่อนที่จะสร้างตัวแปร) InnoDB จะอัพเดตสถิติในระหว่างคำสั่งข้อมูลเมตาเช่น SHOW TABLE STATUS หรือ SHOW INDEX หรือเมื่อเข้าถึงตาราง Information_SCHEMA ตารางหรือสถิติ (การปรับปรุงเหล่านี้คล้ายกับสิ่งที่เกิดขึ้นสำหรับ ANALYZE TABLE.) เมื่อปิดการใช้งาน InnoDB จะไม่อัปเดตสถิติระหว่างการดำเนินการเหล่านี้ การปิดใช้งานตัวแปรนี้สามารถปรับปรุงความเร็วในการเข้าถึงสำหรับสคีมาที่มีตารางหรือดัชนีจำนวนมาก นอกจากนี้ยังสามารถปรับปรุงเสถียรภาพของแผนการดำเนินการสำหรับแบบสอบถามที่เกี่ยวข้องกับตาราง InnoDB

การปิดใช้งานอาจหรือไม่อาจให้การนับที่มีเสถียรภาพมากขึ้นในแง่ของการตั้งค่าแผนอธิบาย มันอาจส่งผลกระทบต่อประสิทธิภาพSELECT COUNT(*) from mydb.mytableในทางที่ดีไม่ดีหรือไม่ดีเลย ลองดูสิ !!!


16

สำหรับผู้เริ่มต้นไม่มีสิ่งเช่น 'จำนวนปัจจุบัน' เพื่อเก็บไว้ในตัวแปร แบบสอบถามที่ชอบSELECT COUNT(*) FROM ...นั้นขึ้นอยู่กับระดับการแยกปัจจุบันและธุรกรรมที่ค้างอยู่พร้อมกันทั้งหมด ขึ้นอยู่กับระดับการแยกแบบสอบถามสามารถดูหรือไม่เห็นแถวที่แทรกหรือลบโดยรอธุรกรรมที่ยังไม่ผ่าน วิธีเดียวที่จะตอบคือการนับแถวที่มองเห็นได้กับธุรกรรมปัจจุบัน

โปรดทราบว่าฉันไม่ได้สัมผัสหัวข้อที่มีหนามมากขึ้นของธุรกรรมที่เกิดขึ้นพร้อมกันที่เริ่มต้นหรือสิ้นสุดในระหว่างการนับ ไม่ต้องพูดถึงการย้อนกลับ ...


1
ตกลงดังนั้นมันขึ้นอยู่กับระดับการแยกที่เหมาะสม แต่ก็ยังสามารถใช้งานได้
Radu Murzea

@SoboLAN มีเหตุผลมากมายที่ทำไมไม่ควร & ไม่สามารถเป็นได้ซึ่งส่วนใหญ่แสดงไว้ข้างต้น คุณจะใช้มันโดยการรักษารายการการนับต่อตารางต่อการเริ่มต้นธุรกรรม (SCN ของ Oracle ใน MySQL) หรือไม่? การจัดการการนับเช่นนั้นจะเป็นค่าใช้จ่ายจำนวนมาก - คิดเกี่ยวกับฐานข้อมูลที่มีเซสชันที่เกิดขึ้นพร้อมกัน 100s หรือ 1,000 ครั้งในแต่ละครั้งที่ทำ INSERTs / DELETEs จำนวนมากในตารางเดียวกัน เป็นไปไม่ได้ที่จะรักษา
Philᵀᴹ

การดำเนินการนี้ค่อนข้างยาก แค่คิดว่าการนับจะต้องคงอยู่ในฐานข้อมูลนั่นหมายถึงที่ใดที่หนึ่งในข้อมูลเมตาและการนับนี้จะต้องได้รับการดูแลโดยทุกธุรกรรมที่แทรกหรือลบแถว คุณจะล็อคข้อมูลเมตานั้นอย่างไร และคุณจะจัดการกับการย้อนกลับอย่างไร อยู่ไกลจากเรื่องเล็กน้อย และผลลัพธ์จะสามารถใช้งานได้สำหรับชุดย่อยของแบบสอบถามที่แคบมาก
Remus Rusanu

3
@JackDouglas ที่น่าสนใจ จากสิ่งที่ฉันเคยเห็นในการCOUNT(*)ค้นหาที่ผ่านมาไม่ค่อยมีใครต้องการในความเป็นจริง & มักจะเป็นผลมาจากความไม่มีประสบการณ์ของนักพัฒนา (นับจำนวนแถวก่อนที่เราจะเลือก!) หรือการออกแบบแอปที่ไม่ดี
Philᵀᴹ

1
@SoboLAN - ไม่ก็ไม่เป็นไร การมีบริการที่อัพเดตตารางสถิติบางประเภทตามช่วงเวลาที่กำหนดไว้ล่วงหน้านั้นดีกว่ามาก ลองนึกภาพว่ามีฐานข้อมูลขนาดใหญ่และผู้ดูแลระบบหลายคนทำการสืบค้นตารางส่วนใหญ่ด้วยSELECT COUNT(*)เพิ่มที่ไม่ได้รับการปรับWHEREให้เหมาะสมกับตารางและคุณจะมีผู้ใช้ไม่กี่คนที่นำ db ไปที่หัวเข่าสำหรับเคาน์เตอร์สถิติที่มีประโยชน์
NB

0

แม้ว่าจะเป็นไปได้ในทางทฤษฎีที่จะสามารถนับจำนวนแถวสำหรับตารางที่กำหนดด้วย InnoDB ได้อย่างแม่นยำ แต่ก็ต้องเสียค่าใช้จ่ายในการล็อคจำนวนมากซึ่งจะส่งผลเสียต่อประสิทธิภาพการทำงาน มันก็จะแตกต่างกันไปตามระดับการแยก

MyISAM ทำการล็อคระดับโต๊ะอยู่แล้วดังนั้นจึงไม่มีค่าใช้จ่ายเพิ่มเติม

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

ฉันไม่เห็นด้วยกับการนับที่ไม่ถูกต้อง การนับเป็นการแสดงภาพรวมของข้อมูลและฉันก็พบว่ามันถูกต้องเสมอ

กล่าวโดยสรุป MySQL ปล่อยให้คุณใช้สิ่งนี้กับ InnoDB คุณสามารถเก็บจำนวนและเพิ่ม / ลดได้หลังจากแต่ละแบบสอบถาม แม้ว่าวิธีแก้ปัญหาที่ง่ายกว่านั้นน่าจะเปลี่ยนมาใช้ MyISAM


2
เป็นไปไม่ได้ที่จะรักษาจำนวนแถวในระบบธุรกรรมให้ถูกต้อง เนื่องจากมี rowcount (และถูกต้อง) ที่แตกต่างกันมากเท่ากับธุรกรรมที่ใช้งานอยู่
a_horse_with_no_name

5
ฉันให้ -1 ที่นี่สำหรับ 'แม้ว่าโซลูชันที่ง่ายกว่าน่าจะเปลี่ยนเป็น MyISAM' ฉันจะไม่แนะนำให้เปลี่ยนเป็น MyISAM เพียงเพื่อให้ได้จำนวนแถว
Derek Downey

@a_horse_with_no_name ดังนั้นคุณตกลงว่าจะมีจำนวนแถวที่ "ถูกต้อง" สำหรับแต่ละธุรกรรม ดูเหมือนเป็นไปได้สำหรับฉัน
Marcus Adams

1
@ การทดสอบฉันไม่เคยพูดว่า "เพียงเพื่อให้ได้จำนวนแถว"
Marcus Adams

@a_horse_with_no_name ดูเหมือนไม่ถูกต้อง แท้จริงเราเป็นเพียงการนับจำนวนแถวเมื่อรายการได้รับความมุ่งมั่นใช่มั้ย?
Pacerier
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.