如何解决如何快速更新单个表中 5000 万多行?
我正在尝试根据另一个表的 SELECT 更新超过 50m 的行,但是这个过程需要超过 15 个小时,我还能做些什么来缩小时间窗口以完成更新。 SQL查询:
start transaction;
UPDATE holdings_all a
INNER JOIN asset b ON a.asset = b.name
SET a.AssetID = b.asset_id;
commit;
表结构:
CREATE TABLE `holdings_all` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,`DateAdded` date NOT NULL,`Fund` char(96) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,`Asset` char(96) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,`AssetID` int(10) unsigned DEFAULT NULL,`Weighting` float DEFAULT NULL,`Ticker` char(10) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,`Style` char(15) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,`FirstBought` date DEFAULT NULL,`SharesOwned` int(11) DEFAULT NULL,`Sector` char(45) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,`Price` decimal(13,6) DEFAULT NULL,`Country` char(25) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,`Currency` char(3) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,`PriceUSD` decimal(13,6) unsigned DEFAULT NULL,PRIMARY KEY (`id`),UNIQUE KEY `DateAddedFundTickerWeighting_UNIQUE` (`DateAdded`,`Fund`,`Ticker`,`Weighting`),KEY `Fund` (`Fund`),KEY `DateAdded` (`DateAdded`),KEY `FundTicker` (`Fund`,`Ticker`),KEY `Asset` (`Asset`),KEY `DateAdded_Fund` (`DateAdded`,`Fund`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
CREATE TABLE `asset` (
`asset_id` int(11) unsigned NOT NULL AUTO_INCREMENT,`name` char(90) DEFAULT NULL,PRIMARY KEY (`asset_id`),UNIQUE KEY `name_UNIQUE` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
my.cnf 文件:
[mysqld]
port=3366
innodb-page-size=65536
innodb_buffer_pool_size=4096M
[client]
port=3366
机器规格:
RAM: 16 GB
CPU: 06 cores
DISK: M.2 1TB
解决方法
这里有很多事情可以尝试,没有特定的顺序。
表中有很多索引,有些可能是多余的。
-
Fund
是多余的(Fund,Ticker)
-
DateAdded
和(DateAdded,Fund)
都是多余的给定(DateAdded,Fund,Ticker,Weighting)
也许不需要其他一些索引。这取决于您需要运行的查询。
编辑:这在您的情况下可能不是那么重要,因为如果您没有使用 UPDATE 语句更改其他列的索引,InnoDB 不需要更新它们。但是,如果您插入或删除,则需要编写它们。感谢@ysth 对此提醒的评论。
您加入的列 holdings_all.Asset
和 asset.name
使用不同的字符集。连接可能使用索引。我建议将所有表更新为相同的字符集和排序规则,并在 utf8mb4 上进行标准化:
ALTER TABLE holdings_all CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;
ALTER TABLE asset CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;
我估计您的表需要大约 57GB 来存储。但是您只有一个 4GB 的缓冲池,并且必须与您拥有的任何其他表共享。这意味着您对整个表的 UPDATE 将不得不一遍又一遍地逐出页面并加载其他页面。这可能在很大程度上取决于您的硬盘驱动器的速度。
您应该升级服务器,使其具有 SSD 或 NVMe 硬盘驱动器,以及足够的 RAM,您可以将缓冲池配置为其当前大小的至少 10 倍。
我知道这需要更昂贵的服务器,但如果您正在处理大规模数据,您应该为该工作使用合适的硬件,否则接受这将需要很长时间。
,如果“更新”意味着“替换整个表”,那么以更快的方式进行:
CREATE TABLE new LIKE holdings_all;
- 使用新数据加载
new
。 RENAME TABLE holdings_all TO old,new TO holdings_all;
DROP TABLE old;
大删除和更新的其他提示和技巧:http://mysql.rjweb.org/doc.php/deletebig
如果它实际上是非静态数据,那么至少将其拆分为两个表——静态信息(股票代码、样式、货币等)和要更新的数据(日期、价格、共享所有权等)。
另外,使用 VARCHAR
,而不是 CHAR
。
第 1 步:将该表拆分为两个表。
- 不变的东西(股票代码、基金、date_bought 等)
- 每周变化的内容(价格、日期等)
请务必为第一个表选择合适的 PRIMARY KEY
(例如 ticker
)。第二个表也有一个 ticker
列,以便它们可以 JOINed
在一起。 PRIMARY KEY
可能是 PRIMARY KEY(ticker,date)
sloooow UPDATE
会变成
INSERT INTO table2 (ticker,date,price,...)
VALUES
(?,?,...)
ON DUPLICATE KEY UPDATE
price = VALUES(price),-- whatever you might need to update
...
或者,由于数据似乎来自另一个表,:
INSERT INTO table2 (ticker,...)
SELECT ticker,...
FROM that_other_table
ON DUPLICATE KEY UPDATE
price = VALUES(price),-- whatever you might need to update
...
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。