2011-10-11 97 views
3

我在写一个需要处理数百万个URL的应用程序。它还需要通过URL进行检索。在没有唯一索引/约束的MySQL中防止重复行?

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

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散列做一下UPS,因为B-tree索引将是对趋于网址非常低效有共同的前缀(InnoDB不支持散列索引)。通过与完整URL进行比较来过滤来自CRC32的重复结果。示例检索查询将如下所示:

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

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

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

编辑:为了更详细地了解使用情况,这是一个实时查找内容以响应URL的表格。通过查找URL,我可以找到URL的内部标识,然后使用它来查找页面的内容。新的URL会一直添加到系统中,我不知道这些URL会在什么时候发布。当引用新的URL时,它们很可能会被引用相同URL的同时请求(可能每秒数百次)抨击,这就是为什么我在添加新内容时担心竞争条件的原因。结果需要是立即的,不能有读滞后(亚秒延迟是好的)。

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

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

+2

如何存储网址(sha1?)的散列副本并索引该字段?使用适当的数据库触发器来更新/填充插入/更新散列时,维护开销将非常小。 –

+0

CRC32是URL的散列。它比SHA1小得多(4个字节与20个字节)。我正在计算它在应用程序方面。 –

+1

确实如此,但只有32位,你大大增加了碰撞的可能性,并因此导致误报。 –

回答

0

你有没有考虑过创建一个唯一索引(url_crc,url)?它可能是'巨大的',但是使用CRC32将会碰到多次碰撞,这可能有助于提高页面检索功能的性能,同时防止重复的URL。

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

+0

不幸的是,引用页面的所有内容必须使用相同的页面ID才能一起出现,而不是“丢失”。由于这些页面ID会在整个系统中传播,因此在将来更改它们将会非常复杂。唯一索引也有长度限制。 –

0

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

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

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

2

你有没有进行基准测试,发现btree是一个问题?我感觉过早的优化。其次,如果你担心所有字符串的开始是相同的,一个答案是首先为你的URL反向最后一个字符编制索引。我不认为MySQL本身可以做到这一点,但是在存储它之前,您可以将应用程序中的数据进行反转。或者就是不要使用MySQL。

相关问题