MySQL中有一张表A,有十几个字段,字段中有索引,大概是1千万条记录。并且这个表是线上服务的表,随时有读写操作。 现在,根据需求,你需要为这张表新建一个字段。你会怎么做?
新建字段还不简单,直接用SQL语句,比如: “alter table tblename add myColumn int(5);”确实挺简单的,不过这一般需要 执行一定的时间。这张表是线上服务的表,由于这个新建字段的操作,会导致数据库处于负荷状态,给前端服务的相应变慢,给用户带来延迟 明显的操作体验。所以,我们需要找到一个方法,在对用户体验影响最小的情况下,完成我们的新建字段的操作。(嗯,我们凌晨三点操作) 我们讨论下不要凌晨三点爬起来操作的方案。
首先,我们新建一张表B,表B的字段属性和表A一模一样,并且加上想要新建的那个字段属性。
然后,我们想办法把表A上的数据复制到表B上。MySQL也有复制表的操作,比如: INSERT INTO 新表(字段1,字段2,…….) SELECT 字段1,字段2,…… FROM 旧表但是,或者使用MySQL的表的导入导出方法,但是这些方法同样会对线上数据库的运行影响很大。所以,我们可以写一个脚本,无论用什么语言。进行循环操作, 每次读取1000(这个值可以自己定一个合适的值)条记录,然后将这1000条记录插入到表B中,直到表A的最后一条记录。ruby中有find_each 这种batch的查找。没错, 这种方式会需要一些时间,也许比上面说的方式需要的时间还多。但是,这种方式对线上数据库的影响是非常小的,可以说是正常的查找操作,对其他用户的使用体验影响很小, 至少是能够让用户接受。如何快速的进行数据库记录的批量插入,请看这里: rails中快速插入简单总结
我们可以知道,第二步操作其实是同步A、B表的数据。在这里,我们需要标记一个时间点。标记在我们开始进行循环插入的时间点t。因为当你在循环插入的时候,表A正在线上跑着, 同样随时会被更改或插入(这里暂不考虑删除的情况)。
半小时后,循环操作结束了。然后,我们对表B进行新建和表A一模一样的索引结构的操作。这一步很容易遗忘,如果忘了,线上切换使用表B的话,没有索引,那就是个灾难。
最后,我们在代码中,切换使用表B。比如在rails中,model A 中 定义self.tablename = ‘b’。然后,写一个脚本(ruby可以写rake文件),把在时间点t之后表A的数据有变动 的记录同步到表B,也许是update操作,也许是插入操作。这个脚本可以根据实际情况多运行几次,比如在切换为表B前运行,然后在切换到表B后再运行,保证表A的数据都同步到了表B。 rails的表中一般都有created_at 和 updated_at 字段,可以根据这量个字段值来同步两个表的记录。这样,整个流程就完成了。所以说,表B的取名也最好取和表A表达意思相近的名称。 如果使用MySQL的rename表命令, rename table A to B; 对于一个一千万条记录有20个左右字段的表,在mac本上,大概需要0.9s, 也许服务器性能强一下,时间会更少。但是,也不能保证这段时间内 没有大量的数据库读写,所以,这个要进行取舍了。
如果不觉得麻烦,还可以将表A新建字段,这时候表A已经服务于线上。如果是通过MySQL交换表A和表B的名称,我测了下,100w的记录的表更改名字需要0.3-0.5秒。如果觉得这时间不影响生产环境的话, 可以选择更改表名。 然后,再把表B同步到表A,在线上再切换到表A用于服务。对于,修改多个字段的名称等对线上数据库服务影响较大的操作, 也许都可以用这个方案的思路解决。
一个提供解决思路的文章: http://www.cnblogs.com/wangtao_20/p/3504395.html
本文完。