ALTER TABLE บนตารางขนาดใหญ่พร้อมคอลัมน์ที่จัดทำดัชนี


14

ฉันมีตารางขนาดใหญ่ที่มีคอลัมน์ VARCHAR (20) และฉันต้องแก้ไขเพื่อให้เป็นคอลัมน์ VARCHAR (50) โดยปกติแล้วการแสดง ALTER TABLE (การเพิ่ม TINYINT) ในตารางนี้จะใช้เวลาประมาณ 90-120 นาทีในการดำเนินการดังนั้นฉันสามารถทำได้ในคืนวันเสาร์หรือวันอาทิตย์เท่านั้นเพื่อหลีกเลี่ยงผลกระทบต่อผู้ใช้ฐานข้อมูล ถ้าเป็นไปได้ฉันต้องการจะทำการแก้ไขก่อนหน้านี้

คอลัมน์ถูกทำดัชนีด้วยซึ่งฉันเข้าใจว่าจะทำให้ ALTER TABLE ช้าลงเนื่องจากต้องสร้างดัชนีใหม่หลังจากแก้ไขความยาวคอลัมน์

แอพพลิเคชั่นเว็บถูกตั้งค่าในสภาพแวดล้อมการจำลองแบบ MySQL (26 ทาสและต้นแบบหนึ่งคน) ฉันจำได้ว่าเมื่ออ่านที่ไหนสักแห่งว่าวิธีหนึ่งคือการดำเนินการ ALTER TABLE บนแต่ละสลาฟก่อน (ลดผลกระทบต่อผู้ใช้) จากนั้นทำสิ่งนี้กับ Master แต่จะไม่ลองทำซ้ำคำสั่ง ALTER TABLE กับทาส?

ดังนั้นคำถามของฉันคือ: วิธีที่ดีที่สุดสำหรับฉันที่จะปรับเปลี่ยนตารางนี้มีการหยุดชะงักขั้นต่ำให้กับผู้ใช้ของฉันคืออะไร?

แก้ไข: ตารางคือ InnoDB


การเพิ่มคอลัมน์จิ๋วจินหมายถึงการเพิ่มคอลัมน์ด้วยค่าเริ่มต้นหรือไม่ เพราะการทำเช่นนั้นบนโต๊ะขนาดใหญ่อาจจะใช้เวลานาน ..
แมเรียน

คำตอบ:


13

หากคุณเป็นนักผจญภัยเล็ก ๆ น้อย ๆ คุณสามารถจัดการเรื่องต่างๆด้วยมือโดยทำการแสดง ALTER TABLE ในขั้นตอนที่คุณเห็น สมมติว่าตารางที่คุณต้องการเปลี่ยนเรียกว่า WorkingTable คุณสามารถทำการเปลี่ยนแปลงตามขั้นตอนดังนี้:

#
#  Script 1
#  Alter table structure of a single column of a large table
#
CREATE TABLE WorkingTableNew LIKE WorkingTable;
ALTER TABLE WorkingTableNew MODIFY BigColumn VARCHAR(50);
INSERT INTO WorkingTableNew SELECT * FROM WorkingTable;
ALTER TABLE WorkingTable RENAME WorkingTableOld;
ALTER TABLE WorkingTableNew RENAME WorkingTable;
DROP TABLE WorkingTableOld;

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

#
#  Script 2
#  Alter table structure of a single column of a large table
#  while preventing it from replicating to slaves
#
SET SQL_LOG_BIN = 0;
CREATE TABLE WorkingTableNew LIKE WorkingTable;
ALTER TABLE WorkingTableNew MODIFY BigColumn VARCHAR(50);
INSERT INTO WorkingTableNew SELECT SQL_NO_CACHE * FROM WorkingTable;
ALTER TABLE WorkingTable RENAME WorkingTableOld;
ALTER TABLE WorkingTableNew RENAME WorkingTable;
DROP TABLE WorkingTableOld;

แต่เดี๋ยวก่อน !!! แล้วข้อมูลใหม่ใดที่เข้ามาในขณะประมวลผลคำสั่งเหล่านี้ ??? การเปลี่ยนชื่อตารางในตอนเริ่มต้นของการดำเนินการควรทำการหลอกลวง ให้เปลี่ยนรหัสนี้เล็กน้อยเพื่อป้องกันการป้อนข้อมูลใหม่ในส่วนที่:

#
#  Script 3
#  Alter table structure of a single column of a large table
#  while preventing it from replicating to slaves
#  and preventing new data from entering into the old table
#
SET SQL_LOG_BIN = 0;
ALTER TABLE WorkingTable RENAME WorkingTableOld;
CREATE TABLE WorkingTableNew LIKE WorkingTableOld;
ALTER TABLE WorkingTableNew MODIFY BigColumn VARCHAR(50);
INSERT INTO WorkingTableNew SELECT SQL_NO_CACHE * FROM WorkingTableOld;
ALTER TABLE WorkingTableNew RENAME WorkingTable;
DROP TABLE WorkingTableOld;
  • สคริปต์ 1 สามารถเรียกใช้งานบนสลาฟใด ๆ ที่ไม่ได้เปิดใช้งานบันทึกไบนารี
  • สคริปต์ 2 สามารถดำเนินการกับสลาฟใด ๆ ที่เปิดใช้งานบันทึกไบนารี
  • สคริปต์ 3 สามารถดำเนินการกับต้นแบบหรือที่อื่นใดก็ได้

ให้มันลอง !!!


2
ปัญหาหนึ่งที่ฉันเห็นคือถ้าตารางมีฟิลด์ 'auto_increment' ฉันทำการทดสอบขั้นพื้นฐานและรู้สึกประหลาดใจที่เห็นว่า CREATE TABLE .. ไม่ต้องการคัดลอกค่า auto_increment ไปยังตารางใหม่
Derek Downey

1
@DTest: จับที่ดีและแสดงความคิดเห็นที่ดี !!! ฉันเชื่อว่าคุณสามารถดึงค่า auto_increment จากคอลัมน์ data_schema.tables AUTO_INCREMENT หากตารางไม่มีฟิลด์ AUTO_INCREMENT คอลัมน์ AUTO_INCREMENT ใน information_schema.tables จะเป็น NULL มิฉะนั้นจะมีค่า AUTO_INCREMENT ที่จำเป็น ฉันเดาว่าสามารถเขียนสคริปต์เพื่อแยกค่าที่ไม่ใช่ค่า NULL และทำ ALTER TABLE WorkingSet AUTO_INCREMENT = <somenumber>; ก่อนเปลี่ยนชื่อตาราง temp กลับเป็น WorkingSet
RolandoMySQLDBA

@RolandoMySQLDBA สิ่งอื่นที่สามารถทำได้คือคุณสร้าง WorkingTableNew โดยการคัดลอกคำสั่ง "แสดงสร้างตาราง WorkingTable" และเปลี่ยนค่าของเขตข้อมูล auto_increment โดยการเพิ่มจำนวนที่ปลอดภัยสำหรับคุณและเปลี่ยนคอลัมน์เป็น varchar (50) ) และจากนั้นทำการเปลี่ยนชื่อทั้งสองในคำสั่งเดียว "เปลี่ยนชื่อตาราง WorkingTable เป็น WorkingTableOld เปลี่ยนชื่อตาราง WorkingTableNew to WorkingTable" การเรียกใช้ทั้งสองเปลี่ยนชื่อในคำสั่งเดียวทำให้มั่นใจได้ว่าจะไม่มีการแทรกล้มเหลว (ทดสอบนี่คือ Prod สำหรับตาราง คุณสามารถทำคำสั่ง "แทรกลงใน ... จาก"
Gautam Somani

4

ฉันเดาจากเอกสารจะเป็นเพียงการเพิ่มข้อ จำกัด ความยาวในvarcharจะไม่ทำให้เกิดปัญหาเช่นเดียวกับการเพิ่มคอลัมน์:

สำหรับการดำเนินการบางอย่างอาจมี ALTER TABLE แบบแทนที่ไม่ต้องใช้ตารางชั่วคราว:

แต่ดูเหมือนว่าจะขัดแย้งกับความคิดเห็นในคำถามSOนี้

แก้ไข

อย่างน้อยใน 5.0 ฉันคิดว่าฉันสามารถยืนยันได้ว่าการเพิ่มความยาวไม่จำเป็นต้องมีตารางชั่วคราว (หรือการดำเนินการอื่น ๆ ที่มีราคาแพงเท่ากัน):

testbed:

create table my_table (id int auto_increment primary key, varchar_val varchar(10));
insert into my_table (varchar_val)
select 'HELLO'
from (select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) s1,
     (select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) s2,
     (select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) s3,
     (select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) s4,
     (select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) s5,
     (select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) s6;

ผลลัพธ์:

alter table my_table modify varchar_val varchar(20);
Query OK, 1000000 rows affected (2.91 sec)

alter table my_table add int_val int;
Query OK, 1000000 rows affected (2.86 sec)

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

3

ฉันคิดว่าฉันพูดถึงมันตั้งแต่ ENGINE=INNODB

หากคุณมีข้อ จำกัด ของคีย์ต่างประเทศคุณไม่สามารถแก้ไขและเปลี่ยนชื่อโดยไม่มีข้อ จำกัด ของคุณชี้ไปที่ตารางเก่า (เปลี่ยนชื่อตอนนี้) คุณจะต้องแก้ไขในภายหลังหรือลดข้อ จำกัด ในช่วงเวลานั้น


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