0

我有两个 MySQL 表,我需要在每个表中插入一条记录。警告:由于重复键冲突,插入可靠表可能会失败。在这种情况下,不应插入任何行。

CREATE TABLE `meanings` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;

CREATE TABLE `aliases` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `meaningId` int(10) unsigned DEFAULT NULL,
  `phrase` varchar(8) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `phrase_UNIQUE` (`phrase`),
  KEY `meaning_IDX` (`meaningId`),
  CONSTRAINT `fk_Synonyms_1` FOREIGN KEY (`meaningId`) REFERENCES `meanings` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB;

简而言之,我需要执行以下操作,但速度更快:

INSERT IGNORE INTO aliases (phrase, meaningId) VALUES ('word1', NULL);
SELECT IF(row_count() > 0, last_insert_id(), 0) INTO @aliasId;
INSERT INTO meanings (id) SELECT null FROM aliases WHERE id=@aliasId;
SELECT IFNULL(last_insert_id(), 0) INTO @meaningId;
UPDATE aliases SET meaningId=@meaningId WHERE id=@aliasId;

我的限制/条件/愿望:

  1. SQL表方案如上(虽然可以添加新的cols)
  2. 避免使用事务
  3. 避免使用DELETE FROM meanings
  4. 考虑许多进程同时做同样的事情
  5. 优化当时平均约 40 个别名的质量插入
  6. 尽量减少锁
  7. 记录是永久的,永远不会被删除

我的想法:

  • trigger after insert舒适的方式是aliases插入一行meanings并更新回插入的行aliases.meaningId,但我担心性能
  • 在最坏的情况下,如果与其他解决方案相比,它们的性能足够好,我可以进行交易。这是一个可以在其他事务中调用的例程,因此最好避免事务。
  • 当时数十次插入的性能是关键因素(使用 PHP)。

问题:将大约 40 条(可能是冲突的)记录插入到表aliases中的相应记录的最快解决方案是什么meaning

似乎这个用例场景非常普遍,以至于我脑海中一直有一些东西在说我可能缺乏一些现有的琐碎解决方案/对这个更优雅和更高效的特定案例的内置支持的知识(然后我想出了解决方案与波纹管)。

注意:该表实际上是 1:n,尽管在此示例中它的行为严格为 1:1。在我的用例中,数据以 1:1 的方式开始其生命周期,但后来它们meaning可以改变......


期望结果的例子

插入前:

SELECT * FROM meanings;

# id
  1 
  2 
  3 

SELECT meaningId, phrase FROM aliases;

# meaningId       phrase
  1               word1
  2               word2
  3               word3

现在我需要以高度优化的方式插入值word3(注意这个已经存在)word4word5所以结果是这样的:

SELECT * FROM meanings;

# id
  1 
  2 
  3 
  4
  5

SELECT meaningId, phrase FROM aliases;

# meaningId       phrase
  1               word1
  2               word2
  3               word3
  4               word4
  5               word5
4

1 回答 1

0

考虑到所有情况和限制,我能想到的最好方法是:

ALTER TABLE `meanings` 
ADD COLUMN `initialAliasId` INT(10) UNSIGNED DEFAULT NULL,
ADD UNIQUE INDEX `initialAliasId_UNIQUE` (`initialAliasId` ASC);

INSERT IGNORE INTO aliases (phrase) VALUES ('word1'), ('word2'), ('word3');

INSERT IGNORE INTO meanings (initialAliasId) SELECT id FROM aliases WHERE meaningId IS NULL;

UPDATE aliases as a LEFT JOIN meanings as m ON (m.initialAliasId = a.id) 
SET a.meaningId = m.id WHERE a.meaningId IS NULL;
  • 同时支持多个插入
  • 最大限度地减少单独请求带来的锁和开销
  • 没有交易/回滚
于 2022-01-09T11:53:35.697 回答