เขตข้อมูลเพิ่มอัตโนมัติ MySQL ตั้งค่าใหม่ด้วยตัวเอง


12

เรามีตาราง MySQL ที่มีเขตข้อมูลที่เพิ่มขึ้นอัตโนมัติตั้งค่าเป็น INT (11) ตารางนี้จัดเก็บรายการงานที่เรียกใช้ในแอปพลิเคชัน ในช่วงเวลาใดก็ตามที่มีอายุการใช้งานของแอปพลิเคชันตารางอาจมีรายการนับพันรายการหรือว่างเปล่าทั้งหมด (เช่นทุกอย่างเสร็จสิ้นแล้ว)

ฟิลด์นี้ไม่ได้เป็นสิ่งสำคัญสำหรับสิ่งอื่น

การเพิ่มอัตโนมัติดูเหมือนว่าจะสุ่มตั้งค่าเป็นศูนย์ถึงแม้ว่าเราจะไม่สามารถดักการรีเซ็ตได้

ปัญหาจะเห็นได้ชัดเพราะเราเห็นเขตข้อมูลที่เพิ่มขึ้นโดยอัตโนมัติพูดถึงระเบียน 600,000 หรือมากกว่านั้นอีกไม่นานเขตข้อมูลที่เพิ่มขึ้นอัตโนมัติดูเหมือนว่าจะทำงานในช่วงต่ำ 1,000

มันเกือบจะเหมือนกับว่าการเพิ่มอัตโนมัติจะรีเซ็ตตัวเองหากตารางว่างเปล่า

เป็นไปได้หรือไม่และหากเป็นเช่นนั้นฉันจะปิดหรือเปลี่ยนวิธีการตั้งค่าใหม่ได้อย่างไร

หากไม่มีใครมีคำอธิบายว่าทำไมมันถึงทำเช่นนี้?

ขอบคุณ!


MySQL รุ่นใด มีการใช้ธุรกรรมหรือการจำลองแบบหรือไม่?
thinice

คำตอบ:


26

ตัวนับการเพิ่มอัตโนมัติถูกเก็บไว้ในหน่วยความจำหลักเท่านั้นไม่ใช่ในดิสก์

http://dev.mysql.com/doc/refman/4.1/en/innodb-auto-increment-handling.html

ด้วยเหตุนี้เมื่อเซอร์วิส (หรือเซิร์ฟเวอร์) รีสตาร์ทสิ่งต่อไปนี้จะเกิดขึ้น:

หลังจากเซิร์ฟเวอร์เริ่มทำงานสำหรับการแทรกครั้งแรกลงในตาราง t InnoDB จะดำเนินการเทียบเท่ากับคำสั่งนี้: SELECT MAX (ai_col) จาก t สำหรับการอัพเดท;

InnoDB จะเพิ่มขึ้นทีละหนึ่งค่าที่เรียกคืนโดยคำสั่งและกำหนดให้กับคอลัมน์และไปที่ตัวนับการเพิ่มขึ้นอัตโนมัติสำหรับตาราง หากตารางว่างเปล่า InnoDB จะใช้ค่า 1

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

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

ดังนั้นเราจึงสร้างแถวรับค่าเพิ่มอัตโนมัติสร้างลบแถวเพื่อรักษาตารางให้สะอาดและส่งต่อค่าไปยังไซต์การชำระเงิน สิ่งที่เราทำเพื่อแก้ไขปัญหาวิธีที่ InnoDB จัดการกับค่า AI คือสิ่งต่อไปนี้:

$query = "INSERT INTO transactions_counter () VALUES ();";
mysql_query($query);
$transactionId = mysql_insert_id();
$previousId = $transactionId - 1;
$query = "DELETE FROM transactions_counter WHERE transactionId='$previousId';";
mysql_query($query); 

สิ่งนี้จะช่วยให้ transactionId ล่าสุดที่สร้างขึ้นเป็นแถวในตารางเสมอโดยไม่ทำให้ตารางไม่จำเป็น

หวังว่าจะช่วยให้คนอื่นที่อาจทำงานนี้

แก้ไข (2018-04-18) :

ดังที่กลเม็ดด้านล่างนี้ปรากฏว่าพฤติกรรมของสิ่งนี้ได้รับการแก้ไขใน MySQL 8.0+

https://dev.mysql.com/worklog/task/?id=6204

ถ้อยคำในบันทึกการทำงานนั้นผิดพลาดมากที่สุด แต่ปรากฏว่า InnoDB ในเวอร์ชันที่ใหม่กว่าเหล่านี้สนับสนุนค่า autoinc ที่คงที่ตลอดการรีบูต

-Gremio


ไม่เลวสำหรับโพสต์แรกครับ +1
ceejayoz

มีความรู้น้อยมากที่นั่น Gremio ขอบคุณ !! ฉันได้นำสิ่งนี้ออกไปอย่างสมบูรณ์และเก็บปัญหาไว้เป็นสิ่งที่ต้องตรวจสอบในภายหลัง แต่กลับมาแก้ปัญหาของคุณได้เลย!
Hooligancat

3

เราพบปัญหานี้แล้วและพบว่าเมื่อตารางเพิ่มประสิทธิภาพทำงานในตารางว่างเปล่าค่าการเพิ่มอัตโนมัติก็ถูกรีเซ็ตเช่นกัน ดูรายงานข้อผิดพลาด MySQLนี้

คุณสามารถทำได้โดยทำดังนี้

ALTER TABLE a AUTO_INCREMENT=3 ENGINE=innoDB;

OPTIMIZE TABLEแทน

ดูเหมือนว่านี่คือสิ่งที่ MySQL ทำภายใน


2

เพียงยิงในที่มืด - หากแอปพลิเคชันใช้ a TRUNCATE TABLEเพื่อล้างตารางออกเมื่อประมวลผลเสร็จแล้วจะรีเซ็ตฟิลด์เพิ่มขึ้นอัตโนมัติ นี่คือการอภิปรายสั้น ๆ เกี่ยวกับคำถาม แม้ว่าลิงก์ดังกล่าวจะระบุว่า InnoDB ไม่ได้รีเซ็ต auto_increments ในการตัดทอน แต่ก็มีรายงานว่าเป็นข้อบกพร่องและแก้ไขไม่กี่ปีที่ผ่านมา

สมมติว่าฉันเดาถูกต้องคุณสามารถเปลี่ยนจากการตัดทอนเป็นลบเพื่อแก้ไขปัญหา


ฉันไม่คิดว่าเราใช้ TRUNCATE แต่เราต้องตรวจสอบเพื่อให้แน่ใจ แม้กระทั่งไปจนถึงการตรวจสอบลงไปที่ระดับไดรเวอร์ PHP เพื่อให้แน่ใจ เราไม่ได้เป็นอย่างนั้น ขอบคุณวิธีแก้ปัญหาที่เป็นไปได้ว่า
Hooligancat

1

เฉพาะการรีเซ็ตค่าดังกล่าวอย่างชัดเจนหรือการปล่อย / สร้างฟิลด์นั้นซ้ำหรือการดำเนินการที่มีความรุนแรงอื่น ๆ ที่คล้ายกันควรรีเซ็ตตัวนับ auto_increment (TRUNCATE เป็นทฤษฎีที่ดีจริงๆ) ดูเหมือนว่าเป็นไปไม่ได้ที่คุณจะใช้ INT 32- บิตในทันทีเมื่อค่าสุดท้ายที่คุณเห็นมีเพียง 600k ไม่ควรรีเซ็ตอย่างแน่นอนเนื่องจากตารางเปล่า คุณอาจมีข้อผิดพลาด mysql หรือบางอย่างในรหัส PHP ของคุณ หรือคนที่อยู่ในห้องถัดไปกำลังเล่นอุบายกับคุณ

คุณสามารถดีบักได้โดยเปิดบันทึกไบนารี่เนื่องจากมันจะมีข้อความเช่นนี้ตลอด:

SET INSERT_ID=3747670/*!*/;

อย่างน้อยคุณก็สามารถเห็นรายละเอียดทุกอย่างของสิ่งที่เกิดขึ้นกับตารางนั้นรวมถึงก่อนที่ตัวนับจะถูกรีเซ็ต


ขอบคุณสำหรับเคล็ดลับการแก้ปัญหา เป็นเวลานานแล้วที่ฉันจำเป็นต้องเช็คอิน Serverfault และฉันขอขอบคุณเคล็ดลับของคุณ
Hooligancat

0

แก้ไขตาราง table_name ENGINE = MyISAM

ทำงานให้ฉัน โต๊ะของเรามีขนาดเล็กมากดังนั้นจึงไม่จำเป็นต้องใช้ InnoDB


คุณต้องการธุรกรรมหรือไม่?
Anthony Rutledge

0

InnoDB ไม่เก็บค่าการเพิ่มอัตโนมัติลงในดิสก์จึงลืมมันเมื่อเซิร์ฟเวอร์ MySQL ปิดการทำงาน เมื่อ MySQL จะเริ่มต้นอีกครั้งเครื่องยนต์ InnoDB SELECT (MAX(id) + 1) AS auto_increment FROM tableคืนมูลค่าที่เพิ่มขึ้นรถยนต์ด้วยวิธีนี้: นี่เป็นข้อผิดพลาดที่ได้รับการแก้ไขใน MySQL เวอร์ชัน 8.0

เปลี่ยนเอ็นจิ้นตารางเพื่อแก้ปัญหา:

ALTER TABLE table ENGINE = MyISAM

หรืออัปเดตเซิร์ฟเวอร์ MySQL เป็นเวอร์ชัน 8.0 เมื่อมีการเปิดตัว

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