我有一个要更改的InnoDB表。该表有约8000万行,并退出了一些索引。
我想更改其中一列的名称并添加更多索引。
- 最快的方法是什么(假设即使停机,我也可能会遭受痛苦-服务器是未使用的从属服务器)?
- 是“普通”
alter table
的最快解决方案吗?
此时,我只关心速度:)
sent_at
,并给它添加一些指标
我有一个要更改的InnoDB表。该表有约8000万行,并退出了一些索引。
我想更改其中一列的名称并添加更多索引。
alter table
的最快解决方案吗?此时,我只关心速度:)
sent_at
,并给它添加一些指标
Answers:
加快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
;
请注意以下事项:
我删除了source_persona_index,因为它是其他4个索引的第一列
我删除了target_persona_index,因为它是其他2个索引的第一列
我删除了target_persona_relation_type_index,因为前两列也在target_persona_relation_type_message_id_index中
确定这样可以处理不必要的索引。有没有低基数的索引?这是确定的方法:
运行以下查询:
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;
根据您的问题,大约有80,000,000行。根据经验,如果所选列的基数大于表行计数的5%,则MySQL Query Optimizer将不使用索引。在这种情况下,将为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;
就是这样吧?不 !!!
如果您的网站一直处于运行状态,则在加载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
接下来,登录到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;
然后,正常重启mysql
$ service mysql restart
现在,如果您无法删除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;
试试看 !!!
CAVEAT:对此操作满意后,可以尽早将旧桌子放下:
mysql> DROP TABLE s_relations_old;
正确答案取决于您使用的MySQL引擎的版本。
如果使用5.6+,则重命名和添加/删除索引是在线执行的,即不复制表的所有数据。
ALTER TABLE
照常使用,重命名和索引删除几乎立即生效,而索引添加则相当快(就像一次读取所有表一样快)。
如果使用5.1+,并且启用了InnoDB插件,则添加/删除索引也将在线。不确定重命名。
如果使用旧版本,ALTER TABLE
它仍然是最快的,但是可能会非常慢,因为所有数据都将重新插入到后台的临时表中。
最后,是时候揭穿神话了。不幸的是,我在这里没有足够的业力来评论答案,但是我认为更正最投票的答案很重要。这是错误的:
根据经验,如果所选列的基数大于表行计数的5%,则MySQL Query Optimizer将不使用索引
实际上是另一回事。
索引对于选择几行很有用,因此重要的是它们具有高基数,这意味着许多不同的值和统计上具有相同值的几行。
RENAME TABLE
即时(按预期),但是CHANGE COLUMN
重命名主键做了完整的复制……7个小时!可能仅因为它是主键?不好。
我对Maria DB 10.1.12遇到了同样的问题,但是在阅读了文档之后,我发现有一个选项可以执行“就地”操作,从而消除了表的副本。使用此选项,alter table非常快。以我为例:
alter table user add column (resettoken varchar(256),
resettoken_date date, resettoken_count int), algorithm=inplace;
这非常快。没有算法选项,它将永远不会终止。
对于列重命名,
ALTER TABLE tablename CHANGE columnname newcolumnname datatype;
应该很好并且不会造成任何停机时间。
对于索引,CREATE INDEX语句将锁定表。如前所述,如果它是一个未使用的奴隶,那不是问题。
另一种选择是创建一个具有适当列名和索引的全新表。然后,您可以将所有数据复制到其中,然后执行一系列
BEGIN TRAN;
ALTER TABLE RENAME tablename tablenameold;
ALTER TABLE RENAME newtablename tablename;
DROP TABLE tablenameold;
COMMIT TRAN;
这样可以最大程度地减少停机时间,但需要临时使用两倍的空间。
我也遇到了这个问题,并使用了以下SQL:
/*on créé la table COPY SANS les nouveaux champs et SANS les FKs */
CREATE TABLE IF NOT EXISTS prestations_copy LIKE prestations;
/* on supprime les FKs de la table actuelle */
ALTER TABLE `prestations`
DROP FOREIGN KEY `fk_prestations_pres_promos`,
DROP FOREIGN KEY `fk_prestations_activites`;
/* on remet les FKs sur la table copy */
ALTER TABLE prestations_copy
ADD CONSTRAINT `fk_prestations_activites` FOREIGN KEY (`act_id`) REFERENCES `activites` (`id`) ON UPDATE NO ACTION ON DELETE NO ACTION,
ADD CONSTRAINT `fk_prestations_pres_promos` FOREIGN KEY (`presp_id`) REFERENCES `pres_promos` (`id`) ON UPDATE NO ACTION ON DELETE NO ACTION;
/* On fait le transfert des données de la table actuelle vers la copy, ATTENTION: il faut le même nombre de colonnes */
INSERT INTO prestations_copy
SELECT * FROM prestations;
/* On modifie notre table copy de la façon que l'on souhaite */
ALTER TABLE `prestations_copy`
ADD COLUMN `seo_mot_clef` VARCHAR(50) NULL;
/* on supprime la table actuelle et renome la copy avec le bon nom de table */
SET FOREIGN_KEY_CHECKS=0;
DROP TABLE prestations;
RENAME TABLE prestations_copy TO prestations;
SET FOREIGN_KEY_CHECKS=1;
我希望它可以帮助某人
问候,
将
SHOW CREATE TABLE tblname\G
,显示需要更改的列,该列的数据类型以及该列的新名称。