Ran*_*Ran 15 mysql innodb alter-table ddl
我有一个要更改的 InnoDB 表。该表有大约 80M 行,并退出了一些索引。
我想更改其中一列的名称并添加更多索引。
alter table
,最快的解决方案吗?这个时候,我只关心速度 :)
Rol*_*DBA 16
加速 ALTER TABLE 的一种可靠方法是删除不必要的索引
以下是加载新版本表的初始步骤
CREATE TABLE s_relations_new LIKE s_relations;
#
# Drop Duplicate Indexes
#
ALTER TABLE s_relations_new
DROP INDEX source_persona_index,
DROP INDEX target_persona_index,
DROP INDEX target_persona_relation_type_index
;
Run Code Online (Sandbox Code Playgroud)
请注意以下事项:
我删除了 source_persona_index 因为它是其他 4 个索引中的第一列
我删除了 target_persona_index 因为它是其他 2 个索引中的第一列
我删除了 target_persona_relation_type_index 因为前 2 列也在 target_persona_relation_type_message_id_index 中
OK 这会处理不必要的索引。是否存在基数较低的索引?这是确定的方法:
运行以下查询:
SELECT COUNT(DISTINCT sent_at) FROM s_relations;
SELECT COUNT(DISTINCT message_id) FROM s_relations;
SELECT COUNT(DISTINCT target_object_id) FROM s_relations;
Run Code Online (Sandbox Code Playgroud)
根据您的问题,大约有 80,000,000 行。根据经验,如果所选列的基数大于表行数的 5%,MySQL 查询优化器将不使用索引。在这种情况下,这将是 4,000,000。
COUNT(DISTINCT sent_at)
> 4,000,000
ALTER TABLE s_relations_new
DROP INDEX sent_at_index;
COUNT(DISTINCT message_id)
> 4,000,000
ALTER TABLE s_relations_new
DROP INDEX message_id_index;
COUNT(DISTINCT target_object_id)
> 4,000,000
ALTER TABLE s_relations_new
DROP INDEX target_object_index;
一旦确定了这些索引的有用或无用,您就可以重新加载数据
#
# Change the Column Name
# Load the Table
#
ALTER TABLE s_relations_new CHANGE sent_at sent_at_new int(11) DEFAULT NULL;
INSERT INTO s_relations_new SELECT * FROM s_relations;
Run Code Online (Sandbox Code Playgroud)
就是这样,对吗?不 !!!
如果您的网站一直在运行,那么在加载 s_relations_new 期间可能会有针对 s_relations 运行的 INSERT。你怎么能找回那些丢失的行?
找到 s_relations_new 中的最大 id,并从 s_relations 中添加该 ID 之后的所有内容。为确保该表被冻结并仅用于此更新,您必须有一点停机时间以获取最后插入到 s_relation_new 中的行。这是你要做的:
在操作系统中,重新启动 mysql,以便除了 root@localhost(禁用 TCP/IP)之外没有其他人可以登录:
$ service mysql restart --skip-networking
Run Code Online (Sandbox Code Playgroud)
接下来,登录到 mysql 并加载最后几行:
mysql> SELECT MAX(id) INTO @maxidnew FROM s_relations_new;
mysql> INSERT INTO s_relations_new SELECT * FROM s_relations WHERE id > @maxidnew;
mysql> ALTER TABLE s_relations RENAME s_relations_old;
mysql> ALTER TABLE s_relations_new RENAME s_relations;
Run Code Online (Sandbox Code Playgroud)
然后,正常重启mysql
$ service mysql restart
Run Code Online (Sandbox Code Playgroud)
现在,如果您不能关闭 mysql,您将不得不对 s_relations 进行诱饵和切换。只需登录到 mysql 并执行以下操作:
mysql> ALTER TABLE s_relations RENAME s_relations_old;
mysql> SELECT MAX(id) INTO @maxidnew FROM s_relations_new;
mysql> INSERT INTO s_relations_new SELECT * FROM s_relations_old WHERE id > @maxidnew;
mysql> ALTER TABLE s_relations_new RENAME s_relations;
Run Code Online (Sandbox Code Playgroud)
试一试 !!!
CAVEAT :一旦您对此操作感到满意,您可以尽早删除旧表:
mysql> DROP TABLE s_relations_old;
Run Code Online (Sandbox Code Playgroud)
mez*_*zis 12
正确答案取决于您使用的 MySQL 引擎的版本。
如果使用 5.6+,重命名和添加/删除索引在线执行,即不复制所有表的数据。
ALTER TABLE
像往常一样使用,重命名和索引删除几乎是即时的,并且索引添加相当快(就像一次读取所有表一样快)。
如果使用 5.1+,并且启用了 InnoDB 插件,添加/删除索引也将在线。不确定重命名。
如果使用旧版本,ALTER TABLE
仍然是最快的——但可能会非常慢,因为你的所有数据都将重新插入到后台的临时表中。
最后,是时候揭穿神话了。不幸的是,我在这里没有足够的业力来评论答案,但我觉得纠正投票最多的答案很重要。这是错误的:
根据经验,如果所选列的基数大于表行数的 5%,MySQL 查询优化器将不使用索引
它实际上是相反的。
索引对于选择几行很有用,因此它们具有高基数很重要,这意味着许多不同的值和具有相同值的统计数据很少的行。