วิธีใดในการเลือกโดยไม่ทำให้เกิดการล็อกใน MySQL?


126

ค้นหา:

SELECT COUNT(online.account_id) cnt from online;

show processlistแต่ตารางออนไลน์มีการแก้ไขโดยเหตุการณ์บ่อยครั้งดังนั้นฉันสามารถดูล็อคโดยการเรียกใช้

มีไวยากรณ์ใดใน MySQL ที่สามารถทำให้คำสั่ง select ไม่ทำให้เกิดการล็อกได้หรือไม่?

และฉันลืมที่จะพูดถึงข้างต้นว่ามันอยู่ในฐานข้อมูลทาสของ MySQL

หลังจากที่ฉันเพิ่มเข้าไปmy.cnf:transaction-isolation = READ-UNCOMMITTED ในทาสจะพบกับข้อผิดพลาด:

ข้อผิดพลาด 'ไม่สามารถบันทึกไบนารีได้ ข้อความ: ระดับธุรกรรม 'READ-UNCOMMITTED' ใน InnoDB ไม่ปลอดภัยสำหรับโหมด binlog 'STATEMENT' ในข้อความค้นหา

มีวิธีที่เข้ากันได้ในการทำเช่นนี้หรือไม่?


3
สำหรับคนอื่น ๆ ที่พบคำถามนี้และมีปัญหากับการล็อกบนโต๊ะ: วิธีที่ mySQL ใช้การล็อกภายในขึ้นอยู่กับเครื่องมือจัดเก็บข้อมูล อ่านคำตอบโดย @zombat ด้านล่าง
Simon Forsberg

คำตอบ:


169

พบบทความชื่อ "MYSQL WITH NOLOCK"

https://web.archive.org/web/20100814144042/http://sqldba.org/articles/22-mysql-with-nolock.aspx

ใน MS SQL Server คุณจะทำสิ่งต่อไปนี้:

SELECT * FROM TABLE_NAME WITH (nolock)

และเทียบเท่ากับ MYSQL คือ

SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED ;
SELECT * FROM TABLE_NAME ;
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ ;

แก้ไข

Michael Miorแนะนำสิ่งต่อไปนี้ (จากความคิดเห็น)

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED ;
SELECT * FROM TABLE_NAME ;
COMMIT ;

54
เพียงแค่แจ้งให้ผู้อ่านทราบในอนาคตว่าคุณอาจต้องการกำจัดSESSIONดังนั้นจึงให้ระดับธุรกรรมใช้กับธุรกรรมถัดไปเท่านั้น COMMITจากนั้นเพียงแทนที่คำสั่งที่สามข้างต้นด้วย นี่จะเป็น noop ในกรณีนี้ แต่มีผลข้างเคียงในการยุติธุรกรรมและรีเซ็ตเป็นระดับการแยกเริ่มต้น
Michael Mior

3
แค่ทราบว่าลิงค์นั้นตายแล้ว ... :(
longda

5
ขออภัยฉันต้องลงคะแนนคำตอบนี้ที่ไม่ได้กล่าวถึงความแตกต่างที่สำคัญมากระหว่าง InnoDB และ MyISAM ที่นี่ ตามที่ระบุไว้ใน @omg ข้างต้นสิ่งนี้จะใช้ได้กับ InnoDB แต่ไม่ใช่สำหรับตาราง MyISAM
Simon Forsberg

4
@Craig แน่นอนมันไม่ถูกต้องที่ MyISAM ไม่ได้ออกล็อคอ่านในระหว่างการค้นหา SELECT - มีเป็นล็อคและฝ่ายตรงข้ามที่จะ InnoDB ล็อคล็อคผู้ที่มีตารางการปิดกั้นทุกล็อค WRITE ร้องขอและคำสั่งที่ตามมาทั้งหมดในระหว่างการดำเนิน คำถามเดิมดูเหมือนจะเกี่ยวกับ InnoDB และระดับการแยกก็ไม่มีอยู่สำหรับ MyISAM เช่นกันเอกสารสำหรับSET TRANSACTIONสถานะคำสั่ง : "คำสั่งนี้กำหนดระดับการแยกธุรกรรมซึ่งใช้สำหรับการดำเนินการบนตาราง InnoDB"
syneticon-dj

1
จุดที่ยอมรับ :-) ฉันพยายามอ้างถึงพฤติกรรมการล็อคของ MyISAM กับ InnoDB จริงๆ โซลูชันตามระดับการแยกเหล่านี้ใช้ไม่ได้กับ MyISAM ซึ่งไม่ใช่การทำธุรกรรมดังนั้นจึงใช้การล็อกตารางแบบธรรมดา MyISAM UPDATE และ DELETE ต้องรอให้การล็อกตารางชัดเจนดังนั้นคิวของ SELECT ที่ตามมาจะอยู่ข้างหลังคำขอเขียนจึงถูกบล็อกจนกว่าการเขียนจะเสร็จสิ้น MyISAM ไม่มี "การอ่านสกปรก" และไม่มีทางที่จะอนุญาตให้การเขียนส่วนใหญ่เกิดขึ้นพร้อมกันกับการอ่านดังนั้นจึงไม่มีประเด็นเกี่ยวกับความคิดเห็นใด ๆ ที่นี่ "ไม่สามารถจัดการกับ MyISAM ได้" ฉันคิดว่านั่นคือสิ่งที่ฉันได้รับ :-)
Craig

24

ถ้าตารางเป็น InnoDB โปรดดูที่http://dev.mysql.com/doc/refman/5.1/en/innodb-consistent-read.html - จะใช้การอ่านที่สอดคล้องกัน (โหมดไม่มีการล็อก) สำหรับ SELECT "ที่ทำ ไม่ระบุ FOR UPDATE หรือ LOCK IN SHARE MODE หากตั้งค่าอ็อพชัน innodb_locks_unsafe_for_binlog และระดับการแยกของธุรกรรมไม่ได้ตั้งค่าเป็น SERIALIZABLE ดังนั้นจึงไม่มีการตั้งค่าการล็อกบนแถวที่อ่านจากตารางที่เลือก "


16

ใช้

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED.

รุ่น 5.0 เอกสารมีที่นี่

เวอร์ชัน 5.1 เอกสารมีที่นี่


2
ขอบคุณฉันคิดว่าใกล้แล้ว แต่คำพูดนี้จะมีผลนานแค่ไหน? ฉันจะใช้คำสั่งนี้ในโปรแกรม PHP และควรรีเซ็ต TRANSACTION ISOLATION LEVEL โดยอัตโนมัติเมื่อการค้นหาเสร็จสิ้น
omg

14

คุณอาจต้องการอ่านหน้านี้ของคู่มือ MySQL วิธีการล็อกตารางขึ้นอยู่กับประเภทของตาราง

MyISAM ใช้การล็อกตารางเพื่อให้ได้ความเร็วในการอ่านสูงมาก แต่ถ้าคุณมีคำสั่ง UPDATE รออยู่ SELECTS ในอนาคตจะเข้าคิวหลัง UPDATE

ตาราง InnoDB ใช้การล็อกระดับแถวและคุณจะไม่มีการล็อกตารางทั้งหมดหลังการอัปเดต มีปัญหาการล็อกประเภทอื่น ๆ ที่เกี่ยวข้องกับ InnoDB แต่คุณอาจพบว่าเหมาะกับความต้องการของคุณ


1
"ตั้งค่าระดับการแยกธุรกรรมที่อ่านไม่ถูกต้อง" จะทำงานกับตาราง MyISAM หรือไม่
omg

5
ตาราง MyISAM ไม่รองรับธุรกรรมในรูปแบบใด ๆ แบบสอบถามเกี่ยวกับธุรกรรมจะทำงานบนตาราง MyISAM ดังนั้นแบบสอบถามที่คุณกล่าวถึงข้างต้นจะดำเนินการ แต่ไม่มีผลใด ๆ
zombat

1
แล้วฉันจะทำอย่างไรเพื่อหลีกเลี่ยงไม่ให้ SELECTS เข้าคิวในกรณีของ MyISAM?
omg

6
ฉันจะทำอย่างไรเพื่อหลีกเลี่ยงไม่ให้ SELECTS เข้าคิวในกรณีของ MyISAM เปลี่ยนไปใช้ innodb MyISAM ใช้การล็อกระดับตารางสำหรับทุกแบบสอบถาม นั่นเป็นข้อบกพร่องที่สำคัญ
Frank Farmer

2

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

อย่างน้อยคุณควรตั้งค่า concurrent_insert เป็น 1 (ค่าเริ่มต้น) จากนั้นหากไม่มี "ช่องว่าง" ในไฟล์ข้อมูลสำหรับการเติมตารางการแทรกจะถูกต่อท้ายไฟล์และ SELECT และ INSERTs สามารถเกิดขึ้นพร้อมกันกับตาราง MyISAM โปรดทราบว่าการลบบันทึกจะทำให้เกิด "ช่องว่าง" ในไฟล์ข้อมูลซึ่งจะพยายามเติมส่วนแทรกและการอัปเดตในอนาคต

หากคุณไม่ค่อยลบระเบียนคุณสามารถตั้งค่า concurrent_insert ให้เท่ากับ 2 และส่วนแทรกจะถูกเพิ่มไว้ท้ายไฟล์ข้อมูลเสมอ จากนั้นการเลือกและการแทรกสามารถเกิดขึ้นได้พร้อมกัน แต่ไฟล์ข้อมูลของคุณจะไม่มีวันเล็กลงไม่ว่าคุณจะลบบันทึกไปกี่รายการก็ตาม (ยกเว้นบันทึกทั้งหมด)

บรรทัดล่างสุดหากคุณมีการอัปเดตแทรกและเลือกบนตารางจำนวนมากคุณควรทำให้เป็น InnoDB คุณสามารถผสมประเภทตารางในระบบได้อย่างอิสระ


1

อีกวิธีหนึ่งในการเปิดใช้งานการอ่านสกปรกใน mysql คือเพิ่มคำใบ้: ล็อกในโหมดแบ่งปัน

SELECT * FROM TABLE_NAME LOCK IN SHARE MODE; 

"ถ้าตั้งค่า autocommit เป็น 1 คำสั่ง LOCK IN SHARE MODE และ FOR UPDATE จะไม่มีผล" ... และ autocommit = 1 เป็นค่าเริ่มต้น
Martin Zvarík

0

จากข้อมูลอ้างอิงนี้ :

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


0

โดยปกติ SELECT จะไม่ทำการล็อกใด ๆ ที่คุณสนใจบนตาราง InnoDB ระดับการแยกธุรกรรมเริ่มต้นหมายความว่าการเลือกไม่ล็อกสิ่งต่างๆ

แน่นอนความขัดแย้งยังคงเกิดขึ้น


1
ฉันรู้ว่าโพสต์นี้เก่า แต่คำตอบนี้กว้างเกินไปและบางครั้งก็เป็นจริงเท่านั้น ดูdev.mysql.com/doc/refman/5.0/en/innodb-locks-set.html ส่วนใหญ่จะได้มาจากการล็อกสำหรับการอ่านขึ้นอยู่กับระดับการแยก โดยเฉพาะอย่างยิ่งในกรณีนี้ผู้โพสต์กำลังจัดการกับฐานข้อมูลที่จำลองแบบและระบุไว้อย่างชัดเจนว่าเขาสามารถใช้show processlistเพื่อดูล็อกได้จริง ดังนั้นจึงปลอดภัยที่จะสันนิษฐานว่ามีการล็อกจริง
Craig

1
คำตอบคือความจริงเสมอ แน่นอนว่ามีการล็อคบางอย่าง - mutexes ภายในบางตัวที่อยู่ภายใน innodb ซึ่งถูกนำมาใช้ (เช่น mutex สระบัฟเฟอร์ innodb) ผู้ใช้ส่วนใหญ่ไม่สนใจหรือสังเกตเห็นการล็อกเหล่านี้และโดยปกติจะโต้แย้งเฉพาะในระหว่างการดำเนินการ DDL เท่านั้น (เช่นหากคุณมีพูลบัฟเฟอร์ 16G และ "วางตาราง" ในเธรดอื่น) แต่จะไม่ใช้การล็อกแถวโดยค่าเริ่มต้น นั่นคือสิ่งที่ฉันหมายถึง. คำตอบค่อนข้างคลุมเครือ
MarkR

1
เสมอ ? จะเกิดอะไรขึ้นถ้าระดับการแยกธุรกรรมถูกตั้งค่าเป็นซีเรียลได้หรือคำสั่ง select ใช้ LOCK IN SHARE MODE และ autocommit ถูกปิดใช้งาน? ฉันรู้ว่าเซิร์ฟเวอร์ฐานข้อมูลจำนวนมาก (ส่วนใหญ่ / ทั้งหมด?) ใช้การแยกสแน็ปช็อตตามค่าเริ่มต้นแทนการทำให้เป็นอนุกรมจริง แต่ยังไม่มีเหตุผลที่จำเป็นสำหรับการบังคับให้อ่านต่อเนื่องเป็นครั้งคราวหรือไม่ แต่ดูเหมือนว่าคุณกำลังพูดว่าในกรณีปกติจากระยะไกลเงื่อนไขเริ่มต้นใน MySQL จะไม่ทำให้เกิดการล็อกการอ่านที่ส่งผลกระทบต่อเธรดอื่น ๆ ดังนั้นอย่ากังวลกับปัญหาที่คุณไม่มี? ฉันพยายามยกเลิกการลงคะแนน BTW ขออภัย ...
Craig

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