【发布时间】:2021-03-02 21:36:27
【问题描述】:
我正在尝试改进公司数据库中某个表的性能。此表有 7.9 690 万行,格式为:
mysql> show fields from BroadcastLog;
+---------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| broadcast_id | int(10) unsigned | YES | MUL | NULL | |
| author_id | int(10) unsigned | YES | MUL | NULL | |
| type | int(11) | NO | MUL | NULL | |
| origin | int(11) | YES | MUL | NULL | |
| date_created | datetime | NO | MUL | NULL | |
| date_modified | datetime | NO | | NULL | |
| old_status | int(10) unsigned | YES | MUL | NULL | |
| new_status | int(10) unsigned | YES | MUL | NULL | |
| json_data | text | YES | | NULL | |
| log_text | text | YES | | NULL | |
+---------------+------------------+------+-----+---------+----------------+
11 rows in set (0.01 sec)
我首先想改进的地方之一是将两个 text 字段更改为 varchar 字段,我知道这通常更有效。于是我试了一下:
mysql> alter table BroadcastLog modify log_text varchar(2048);
Query OK, 0 rows affected, 1 warning (1 min 13.08 sec)
Records: 0 Duplicates: 0 Warnings: 1
mysql> show warnings;
+-------+------+---------------------------------------------------+
| Level | Code | Message |
+-------+------+---------------------------------------------------+
| Note | 1246 | Converting column 'log_text' from VARCHAR to TEXT |
+-------+------+---------------------------------------------------+
1 row in set (0.01 sec)
它没有转换!
我试图变得聪明。让我们创建一个新(临时)列,复制数据,删除旧列,然后重命名新列:
mysql> alter table BroadcastLog add column log_text_vc varchar(2048);
Query OK, 0 rows affected, 1 warning (1 min 13.08 sec)
Records: 0 Duplicates: 0 Warnings: 1
mysql> show warnings;
+-------+------+---------------------------------------------------+
| Level | Code | Message |
+-------+------+---------------------------------------------------+
| Note | 1246 | Converting column 'log_text' from VARCHAR to TEXT |
+-------+------+---------------------------------------------------+
1 row in set (0.01 sec)
甚至无法创建新列!
我试图变得更聪明。创建一个新表,复制数据,删除旧列,将数据复制回来:
mysql> create table tmp (id INT UNSIGNED PRIMARY KEY, json_data VARCHAR(1024), log_text VARCHAR(2048));
Query OK, 0 rows affected (0.04 sec)
mysql> insert into tmp (id, json_data, log_text) select id, json_data, log_text from BroadcastLog;
Query OK, 6939076 rows affected (5 min 28.12 sec)
Records: 6939076 Duplicates: 0 Warnings: 0
mysql> alter table BroadcastLog drop column json_data;
Query OK, 0 rows affected (1 min 12.36 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> alter table BroadcastLog drop column log_text;
Query OK, 0 rows affected (1 min 9.10 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> alter table BroadcastLog add column json_data varchar(1024);
Query OK, 0 rows affected (1 min 11.52 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> alter table BroadcastLog add column log_text varchar(2048);
Query OK, 0 rows affected (1 min 15.41 sec)
Records: 0 Duplicates: 0 Warnings: 1
mysql> show warnings;
+-------+------+---------------------------------------------------+
| Level | Code | Message |
+-------+------+---------------------------------------------------+
| Note | 1246 | Converting column 'log_text' from VARCHAR to TEXT |
+-------+------+---------------------------------------------------+
1 row in set (0.01 sec)
mysql> show fields from BroadcastLog;
+---------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| broadcast_id | int(10) unsigned | YES | MUL | NULL | |
| author_id | int(10) unsigned | YES | MUL | NULL | |
| type | int(11) | NO | MUL | NULL | |
| origin | int(11) | YES | MUL | NULL | |
| date_created | datetime | NO | MUL | NULL | |
| date_modified | datetime | NO | | NULL | |
| old_status | int(10) unsigned | YES | MUL | NULL | |
| new_status | int(10) unsigned | YES | MUL | NULL | |
| json_data | varchar(1024) | YES | | NULL | |
| log_text | mediumtext | YES | | NULL | |
+---------------+------------------+------+-----+---------+----------------+
11 rows in set (0.01 sec)
因此,一个字段已正确创建,但另一个字段仍然转换为 TEXT,尽管该字段完全为空且其中没有数据
我一直在谷歌搜索试图找到答案,但到目前为止我什么也没找到。
创建表语句
根据 cmets,这是 create table 语句(在我进行上述更改后,我本地数据库上的 log_text 列和 json_data 列可能与我今天早上从生产数据库中提取的原始数据不匹配):
mysql> show create table BroadcastLog\G
*************************** 1. row ***************************
Table: BroadcastLog
Create Table: CREATE TABLE `BroadcastLog` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`broadcast_id` int(10) unsigned DEFAULT NULL,
`author_id` int(10) unsigned DEFAULT NULL,
`type` int(11) NOT NULL,
`origin` int(11) DEFAULT NULL,
`date_created` datetime NOT NULL,
`date_modified` datetime NOT NULL,
`old_status` int(10) unsigned DEFAULT NULL,
`new_status` int(10) unsigned DEFAULT NULL,
`log_text` mediumtext,
PRIMARY KEY (`id`),
KEY `old_status` (`old_status`),
KEY `new_status` (`new_status`),
KEY `broadcast_id` (`broadcast_id`),
KEY `author_id` (`author_id`),
KEY `log_type_and_origin` (`type`,`origin`),
KEY `log_origin` (`origin`),
KEY `bl_date_created` (`date_created`),
CONSTRAINT `fk_BroadcastLog_author_id` FOREIGN KEY (`author_id`) REFERENCES `User` (`id`) ON DELETE SET NULL ON UPDATE CASCADE,
CONSTRAINT `fk_BroadcastLog_broadcast_id` FOREIGN KEY (`broadcast_id`) REFERENCES `Broadcast` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=6941898 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
MySQL 版本
mysql> select version();
+-----------+
| version() |
+-----------+
| 5.7.31 |
+-----------+
1 row in set (0.01 sec)
更新
我更新了 MySQL 并得到了相同的结果:
mysql> select version();
+-----------+
| version() |
+-----------+
| 8.0.23 |
+-----------+
1 row in set (0.00 sec)
mysql> show fields from BroadcastLog;
+---------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+--------------+------+-----+---------+----------------+
| id | int unsigned | NO | PRI | NULL | auto_increment |
| broadcast_id | int unsigned | YES | MUL | NULL | |
| author_id | int unsigned | YES | MUL | NULL | |
| type | int | NO | MUL | NULL | |
| origin | int | YES | MUL | NULL | |
| date_created | datetime | NO | | NULL | |
| date_modified | datetime | NO | | NULL | |
| log_text | text | NO | | NULL | |
| json_data | text | YES | | NULL | |
| old_status | int unsigned | YES | MUL | NULL | |
| new_status | int unsigned | YES | MUL | NULL | |
+---------------+--------------+------+-----+---------+----------------+
11 rows in set (0.00 sec)
mysql> alter table BroadcastLog modify log_text varchar(2048);
Query OK, 6939076 rows affected, 1 warning (3 min 22.64 sec)
Records: 6939076 Duplicates: 0 Warnings: 1
mysql> show warnings;
+-------+------+---------------------------------------------------+
| Level | Code | Message |
+-------+------+---------------------------------------------------+
| Note | 1246 | Converting column 'log_text' from VARCHAR to TEXT |
+-------+------+---------------------------------------------------+
1 row in set (0.01 sec)
mysql> show fields from BroadcastLog;
+---------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+--------------+------+-----+---------+----------------+
| id | int unsigned | NO | PRI | NULL | auto_increment |
| broadcast_id | int unsigned | YES | MUL | NULL | |
| author_id | int unsigned | YES | MUL | NULL | |
| type | int | NO | MUL | NULL | |
| origin | int | YES | MUL | NULL | |
| date_created | datetime | NO | | NULL | |
| date_modified | datetime | NO | | NULL | |
| log_text | mediumtext | YES | | NULL | |
| json_data | text | YES | | NULL | |
| old_status | int unsigned | YES | MUL | NULL | |
| new_status | int unsigned | YES | MUL | NULL | |
+---------------+--------------+------+-----+---------+----------------+
11 rows in set (0.02 sec)
我会注意到我在输出中看到的一个差异,这可能有另一种解释:它现在是“6939076 行受影响”而不是“0 行受影响”。尽管我花了几个小时试图理解这种行为,但在我开始这个 SO 线程之前,我已经运行了 multiple ALTER TABLE 语句。第一次尝试更改列时,您可能只会影响行,而我只是错过了它。也有可能 MySQL 8 只是对“受影响的行”使用了不同的度量标准并且具有不同的输出。
无论如何,由于某种原因仍未转换为 VARCHAR
【问题讨论】:
-
你能显示完整的
SHOW CREATE TABLE版本而不是DESCRIBE的摘要吗?提示:使用\G获得干净的输出。如果这是一个 MyISAM 表,那可能会解释很多。 -
我用我的 CREATE TABLE 语句更新了帖子。这是一个 InnoDB 表,而不是 MyISAM。
-
下一个逻辑问题:什么 MySQL 版本?支持 >255 个字符是“新”事物,因此如果您使用的是 5.6 或更早版本,它可能无法正常工作。
-
@tadman 再次更新 MySQL 版本。现在是 5.7.31
-
@tadman 今天早上我用 MySQL 8.0 进行了测试——仍然无法转换为
VARCHAR。没有解释为什么
标签: mysql