在没有唯一索引/约束的情况下防止MySQL中的重复行?

时间:2011-10-11 19:33:04

标签: mysql sql query-optimization

我正在编写一个需要处理数百万个URL的应用程序。它还需要通过URL进行检索。

我的表目前看起来像这样:

CREATE TABLE Pages (
  id bigint(20) unsigned NOT NULL,
  url varchar(4096) COLLATE utf8_unicode_ci NOT NULL,
  url_crc int(11) NOT NULL,
  PRIMARY KEY (id),
  KEY url_crc (url_crc)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

这种结构背后的想法是通过URL的CRC32哈希进行查找,因为对于往往具有公共前缀的URL(InnoDB不支持哈希索引),b树索引效率非常低。通过与完整URL的比较来过滤CRC32的重复结果。示例检索查询如下所示:

SELECT id
FROM Pages
WHERE url_crc = 2842100667
  AND url = 'example.com/page.html';

我遇到的问题是避免插入重复的条目。在插入新条目之前,应用程序将始终检查数据库中是否存在现有条目,但在我的应用程序中,可能会同时对同一个新URL进行多次查询,并且将输入重复的CRC32和URL。

我不想在url上创建一个唯一的索引,因为它将是巨大的。我也不想在每个插入上写入锁定表,因为这会破坏并发插入性能。有没有一种有效的方法来解决这个问题?

编辑:要详细了解使用情况,它是一个实时表,用于查找内容以响应URL。通过查找URL,我可以找到URL的内部ID,然后使用它来查找页面的内容。系统会一直添加新的网址,我不知道这些网址会是什么。当引用新URL时,它们可能会被引用相同URL的同时请求(可能每秒数百个)猛烈抨击,这就是我在添加新内容时关注竞争条件的原因。结果必须是立即的,并且不能有读滞后(亚秒滞后是可以的)。

首先,新网址每天只会增加几千个,但在我们明年有时间转向更具可扩展性的解决方案之前,系统需要处理多次。

仅在url上使用唯一索引的另一个问题是URL的长度可能超过唯一索引的最大长度。即使我删除CRC32技巧,它也无法解决防止重复URL的问题。

3 个答案:

答案 0 :(得分:2)

您是否真的进行了基准测试并发现btree是一个问题?我觉得过早优化。

其次,如果你担心所有字符串的开头是相同的,那么一个答案就是首先将你的URL反转 - 最后一个字符。我不认为MySQL可以原生地执行此操作,但您可以在存储之前撤消应用程序中的数据。或者只是不要使用MySQL。

答案 1 :(得分:0)

您是否考虑过创建UNIQUE INDEX(url_crc,url)?它可能是“巨大的”,但是使用CRC32会产生冲突的次数,它可能有助于提高页面检索功能,同时还可以防止重复的网址。

要考虑的另一件事是允许插入重复项,并使用脚本每晚删除它们(或者每当流量很低时)。

答案 2 :(得分:0)

除了Pages表之外,还要创建3个具有相同列的其他表(PagesInsertA,PagesInsertB和PagesInsertC)。插入URL时,请检查现有条目的Pages,如果不存在,请将URL插入PagesInsertA。您可以在该较小的表上使用唯一索引,也可以包含稍后删除重复项的步骤(在下面讨论)。在轮换时间结束时(可能需要一分钟,请参阅下面的约束讨论),切换到将新URL插入PagesInsertB。在PagesInsertA上执行以下步骤:删除重复项(如果您没有使用唯一索引),删除任何重复PagesInsertC中的条目的条目(该表将在第一次为空,但不是第二次),添加来自PagesInsertA to Pages,空PagesInsertC。

在第二个句点结束时,切换到在PagesInsertC中插入新的URL。在PagesInsertB上执行上面讨论的步骤(唯一的区别是您将删除在PagesInsertA中找到的条目并在末尾清空PagesInsertA)。继续旋转新URL插入的表格(A - > B - > C - > A - > ...)。

至少需要3个插入表,因为将URL插入到新插入表和将前一个插入表中已清理的行插入Pages中之间会有延迟。在本例中,我使用1分钟作为开关之间的时间,但只要从PagesInsertA插入到Pages并清空PagesInsertC(例如),就可以在将新URL插入PagesInsertB和PagesInsertC之间切换之前将时间缩短。