Mysql优化算法,保存历史变化的变种
Posted
技术标签:
【中文标题】Mysql优化算法,保存历史变化的变种【英文标题】:Mysql optimization algorithm , variants to saving changes in history 【发布时间】:2019-02-25 09:32:31 【问题描述】:我有一个包含实际用户信息的主表
CREATE TABLE user
(
id bigint NOT NULL
PRIMARY KEY,
updated timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL ON UPDATE CURRENT_TIMESTAMP,
username varchar(40) NULL,
full_name varchar(255) NULL,
biography varchar(512) NULL,
profile_pic_id varchar(40) NULL,
profile_pic_url varchar(255) NULL,
hd_profile_pic_url varchar(255) NULL,
follower_count int NULL,
following_count int NULL,
media_count int NULL,
usertags_count int NULL,
following_tag_count int NULL,
external_url longtext NULL,
reel_auto_archive varchar(255) NULL,
has_biography_translation tinyint(1) NULL,
has_anonymous_profile_picture tinyint(1) NULL,
has_highlight_reels tinyint(1) NULL,
is_business tinyint(1) NULL,
is_active tinyint(1) NULL,
is_verified tinyint(1) NULL,
is_private tinyint(1) NULL,
is_blocked tinyint(1) NULL
)
用于保存历史记录的表也几乎相同:
CREATE TABLE user_history
(
id int AUTO_INCREMENT
PRIMARY KEY,
user_id bigint NULL,
added timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL,
username varchar(40) NULL,
full_name varchar(255) NULL,
biography varchar(512) NULL,
profile_pic_id varchar(40) NULL,
profile_pic_url varchar(255) NULL,
hd_profile_pic_url varchar(255) NULL,
follower_count int NULL,
following_count int NULL,
media_count int NULL,
usertags_count int NULL,
following_tag_count int NULL,
external_url longtext NULL,
reel_auto_archive varchar(255) NULL,
has_biography_translation tinyint(1) NULL,
has_anonymous_profile_picture tinyint(1) NULL,
has_highlight_reels tinyint(1) NULL,
is_business tinyint(1) NULL,
is_active tinyint(1) NULL,
is_verified tinyint(1) NULL,
is_private tinyint(1) NULL,
is_blocked tinyint(1) NULL,
CONSTRAINT FK_F19A7E3C5AFE2D44
FOREIGN KEY (user_id) REFERENCES user (id)
)
COLLATE = utf8mb4_unicode_ci;
CREATE INDEX IDX_F19A7E3C5AFE2D44
ON user_history (user_id);
用于保存历史记录的触发器:
CREATE TRIGGER user_update
AFTER UPDATE
ON user
FOR EACH ROW
BEGIN
INSERT INTO `user_history` (`user_id`, `username`, `full_name`, `biography`, `profile_pic_id`,
`profile_pic_url`, `hd_profile_pic_url`, `follower_count`, `following_count`,
`media_count`, `usertags_count`, `following_tag_count`, `external_url`,
`reel_auto_archive`, `has_biography_translation`, `has_anonymous_profile_picture`,
`has_highlight_reels`, `is_business`, `is_active`, `is_verified`, `is_private`,
`is_blocked`, `added`)
VALUES (NEW.id, NEW.username, NEW.full_name, NEW.biography, NEW.profile_pic_id, NEW.profile_pic_url,
NEW.hd_profile_pic_url,
NEW.follower_count, NEW.following_count, NEW.media_count, NEW.usertags_count, NEW.following_tag_count,
NEW.external_url,
NEW.reel_auto_archive, NEW.has_biography_translation, NEW.has_anonymous_profile_picture,
NEW.has_highlight_reels,
NEW.is_business, NEW.is_active, NEW.is_verified, NEW.is_private, NEW.is_blocked, now());
END;
所以我有一些问题:
有什么变种可以提高写入速度以保存历史记录? 我尝试通过 LOAD DATA LOCAL INFILE,而不是触发 - 没有速度提升。
有哪些变体可以用更少的数据量保存历史记录? 例如,我想我可以运行一些外部脚本,检查 user_history 数据并将相同的值设置为 NULL? 我尝试使用另一个触发器来保存 - 更大,它在写入之前比较每个值并只写入更改的值 - 但它的执行时间很长。 还是将数据差异(可重用)保存到数据库的任何更好的变体?
【问题讨论】:
System versioned tables @danblack 看起来只有 MariaDB 才有这个功能,对吗?我用的是 mysql 8.0 在 MySQL 中不支持像 danblack sent 这样的临时表。 澄清:这是否正确:user
始终包含 当前 值,user_history
有完整的历史记录?
【参考方案1】:
将计数器移动到并行表。将这些拆分出来也可以加快UPDATE
的速度。 “UPDATE”的内部处理涉及构建整个行的副本,以防其他连接访问同一行。
此外,不要保留它们的历史记录。我猜大多数“更新”都在这个领域。
将所有has_%
和is_%
标志组合成一个SET
或TINYINT UNSIGNED
(最多8 个,或SMALLINT UNSIGNED
最多16 个)。
没有has
标志——当您需要检查“有”时,只需使用LEFT JOIN
或EXISTS
。
制作is_%
标志(可能还有其他列)NOT NULL
。一般来说,您应该有意识地确定NULL
是否对业务逻辑有意义,而不是简单地将所有列都设为NULLable
。
(我的建议适用于所有版本的 MySQL 和 MariaDB。)
【讨论】:
以上是关于Mysql优化算法,保存历史变化的变种的主要内容,如果未能解决你的问题,请参考以下文章
Mysql 优化器内部JOIN算法hash join Nestloopjoin及classic hash join CHJ过程详解