记录生产中遇到的问题
MySql对大数据表进行alter操作导致的问题
Mysql执行DDL直接修改表结构的过程中可能会锁表,导致无法写数据,出现生产事故。
MySql各版本执行DDL方式
- Copy Table(5.5之前):通过临时表拷贝的方式实现的:新建一个带有新结构的临时表,将原表数据全部拷贝到临时表,然后Rename。【过程原表可读不可写】
- Inplace(5.5):直接在原表上执行DDL,但仅支持添加、删除索引两种方式。【过程原表可读不可写】
- Online(5.6):通过全量+增量的方式实现,直接在原表上执行DDL。
- 【如添加普通列|不存在全文索引时可读可写】
- 【修改列类型DDL|添加auto_increment列|修改字符集|存在全文索引时可读不可写】
- 【存在慢SQL或者较大的结果集的SQL在运行|存在一个事务在操作表可读不可写】
详细参见:
方案1:创建新表进行alter并复制数据
[选择在凌晨3-4时更新]
- 首先创建新的临时表,表结构通过命令ALTAR TABLE新定义的结构,索引
- 然后把原表中数据导入到临时表
- 记录最后一条更新数据的索引,统计更新数量
- 使用脚本对之前的数据进行小数据批量分批复制到临时表(走task或者脚本,记得复制id)
- 更新完成,对之前更新数据索引后增加的数据进行复制(transaction)
- 删除原表
- 最后把临时表重命名为原来的表名
实践代码
-- 1. 要修改结构的大数据表
DROP TABLE
IF EXISTS USER;
CREATE TABLE `user` (
`id` INT (11) NOT NULL AUTO_INCREMENT,
`username` VARCHAR (32) NOT NULL DEFAULT '',
`age` INT (11) NOT NULL DEFAULT '-1',
PRIMARY KEY (`id`)
) ENGINE = INNODB DEFAULT CHARSET = utf8mb4 COMMENT = '用户表';
-- 2. 新表结构
DROP TABLE
IF EXISTS user_tmp;
CREATE TABLE `user_tmp` (
`id` INT (11) NOT NULL AUTO_INCREMENT,
`username` VARCHAR (32) NOT NULL DEFAULT '',
`age` INT (11) NOT NULL DEFAULT '-1',
PRIMARY KEY (`id`),
INDEX `index_username_age` (`username`, `age`)
) ENGINE = INNODB DEFAULT CHARSET = utf8mb4 COMMENT = '用户表';
-- 3.创建存储过程 max_id为 去最近更新的一条数据的id 11265265
DELIMITER $$
DROP PROCEDURE
IF EXISTS copy_data ; CREATE PROCEDURE copy_data ()
BEGIN
DECLARE limitSize BIGINT ;
DECLARE beginId BIGINT ;
DECLARE endId BIGINT ;
DECLARE maxId BIGINT ;
SET limitSize = 5000 ;
SET beginId = 0 ;
SET endId = limitSize ;
SET maxId = 11265265 ;
WHILE beginId < maxId DO
IF endId > maxId THEN
SET endId = maxId ;
END
IF ; INSERT INTO user_tmp (`id`, `username`, `age`) SELECT
`id`,
`username`,
`age`
FROM
USER
WHERE
id >= beginId
AND id < endId ;
SET beginId = endId ;
SET endId = endId + limitSize ;
END
WHILE ;
END ;$$
DELIMITER ;
-- 4.执行存储过程
CALL copy_data ();
-- 5. 同步剩下的数据,并修改表名称,将临时表修改为新表
START TRANSACTION;
INSERT INTO user_tmp (`id`, `username`, `age`) SELECT
`id`,
`username`,
`age`
FROM
USER
WHERE
id >= 1265265;
ALTER TABLE USER RENAME user_old;
ALTER TABLE user_tmp RENAME USER;
COMMIT;
方案2:新建一个表与旧表进行字段关联
略…
注意点
如果项目使用了Hibernate,需要关闭hibernate ddl(删掉hibernate.hbm2ddl.auto)。hibernate.cfg.xml 中hibernate.hbm2ddl.auto配置节点:
<property name="hibernate.hbm2ddl.auto" value="create" />
- hibernate.hbm2ddl.auto参数的作用主要用于:自动创建|更新|验证数据库表结构
- create 每次加载hibernate时都会删除上一次的生成的表,然后根据你的model类再重新来生成新表
- create-drop 每次加载hibernate时根据model类生成表,但是sessionFactory一关闭,表就自动删除。
- update 第一次加载hibernate时根据model类会自动建立起表的结构(前提是先建立好数据库),以后加载hibernate时根据 model类自动更新表结构,即使表结构改变了但表中的行仍然存在不会删除以前的行。
- validate 每次加载hibernate时,验证创建数据库表结构,只会和数据库中的表进行比较,不会创建新表,但是会插入新值。