【问题标题】:Web Crawler Url Storing At Database - Fast URL Lookup - Hashing - C#Web 爬虫 URL 存储在数据库中 - 快速 URL 查找 - 散列 - C#
【发布时间】:2012-02-07 19:13:06
【问题描述】:

我正在编写自己的网络爬虫。目前我将网址直接存储为 uri.absoluteurl 。因此,当我查询数据库是否已添加该 url 时,我直接将数据库查询为 select pageid from mytable where url='absoluteurl' 。我想这会对数据库造成额外的压力,因为我的核心 i 7 @ 4.5 ghz cpu 几乎一直处于 100%。

所以我想到如果我还在数据库中存储 url 的 md5 哈希并查找它们是否存在该 url 可以提高查找速度。

所以等待你的想法。要检查该 url 是否存在于数据库中,最好的方法是什么?

c# 4.0,MS-sql 2008

示例:

【问题讨论】:

  • 我怀疑 url 列上有索引?
  • Eugen Rieck 请看这张图片:img62.imageshack.us/img62/589/exampleimage.png
  • 这看起来确实很奇怪 - 查询计划显示 CPU 可忽略不计,一些 I/O 成本(这是我所期望的)。 CPU 使用的是用户态还是内核?
  • CPU 正在使用 Windows 7 Ultimate 64 位管理员帐户。 Core i7 2600k 8 核 @ 4.5 GHZ。我可以清楚地说这个查询成本太高了。所以我想知道某种散列可以加快速度。
  • 再次:用户空间还是内核? (在任务管理器中选择“查看”->“显示内核时间”或类似)

标签: c# sql url web-crawler store


【解决方案1】:

由于您已经在 Url 列上创建了索引,因此我猜测是 SELECT(获取 pageid),那么如果它不存在,则 INSERT(新 URL)是导致 CPU 达到峰值的原因。如果你的爬虫有多个线程,你可能会对 tblPages 上的 SQL 中的并发/锁定机制征税。

关于您的具体问题,我会使用 CHECKSUM (crc) 而不是 HASHBYTES (md)。 CHECKSUM 更快,它返回 INT 而不是 VARBINARY,因此索引会更容易/更快。

但是,正是因为 CHECKSUM 返回一个 INT,所以很容易发生冲突,因此您还应该将 URL 作为 AND 子句进行搜索。

SELECT PageId FROM tblPages WHERE HashedUrl=CHECKSUM(@url) AND PageUrl=@url

现在只在 HashedUrl(而不是 PageUrl)上放置一个列索引。由于可能发生冲突,索引必须是非唯一的。这将为您提供最快的 INSERT 和 SELECT,直到您开始获得超过 40 亿的表行数,在这种情况下,INT CHECKSUM 冲突的数量将导致对未索引的 PageUrl 列进行大量的部分表扫描。

更新

这是我使用的简单基准代码

GO
/* NORMAL METHOD */
BEGIN
SET STATISTICS TIME ON
--
IF EXISTS(SELECT * FROM tempdb.dbo.sysobjects WHERE ID = OBJECT_ID(N'tempdb..#Store1'))
BEGIN
    DROP TABLE #Store1
END
-- Normal
CREATE TABLE #Store1 (Id INT IDENTITY(1,1) PRIMARY KEY NONCLUSTERED, Data VARCHAR(4000))
CREATE UNIQUE CLUSTERED INDEX CIX_STORE1_DATA ON #Store1(Data)
-- Help Create Data
DECLARE @Data TABLE(Data VARCHAR(4000))
INSERT INTO @Data(Data) VALUES ('red.'), ('YELLOW/'), ('green'), ('.BLUE'), ('/violet'), ('PURPLE-'), ('-orange')
-- The data set we'll use for testing
INSERT INTO @Data
    SELECT a.Data + b.Data + c.Data + d.Data + e.Data + f.Data + g.Data 
    FROM @Data a, @Data b, @Data c, @Data d, @Data e, @Data f, @Data g
-- INSERTION TESTS
PRINT('INSERT INTO NORMAL')
INSERT INTO #Store1(Data)
    SELECT Data FROM @Data
-- SELECTION TESTS
PRINT('SELECT FROM NORMAL')
SELECT TOP 5000 d.Data, (SELECT s.Id FROM #Store1 s WHERE s.Data = d.Data) FROM @Data d 
--
SET STATISTICS TIME OFF
END 
GO
/* USING YOUR OWN CHECKSUM/HASH */
BEGIN
SET STATISTICS TIME ON
--
IF EXISTS(SELECT * FROM tempdb.dbo.sysobjects WHERE ID = OBJECT_ID(N'tempdb..#Store2'))
BEGIN
    DROP TABLE #Store2
END
-- With Hash
CREATE TABLE #Store2 (Id INT IDENTITY(1,1) PRIMARY KEY NONCLUSTERED, Hsh INT, Data VARCHAR(4000))
CREATE CLUSTERED INDEX CIX_STORE2_CRC ON #Store2(Hsh)
-- Help Create Data
DECLARE @Data TABLE(Data VARCHAR(4000))
INSERT INTO @Data(Data) VALUES ('red.'), ('YELLOW/'), ('green'), ('.BLUE'), ('/violet'), ('PURPLE-'), ('-orange')
-- The data set we'll use for testing
INSERT INTO @Data
    SELECT a.Data + b.Data + c.Data + d.Data + e.Data + f.Data + g.Data 
    FROM @Data a, @Data b, @Data c, @Data d, @Data e, @Data f, @Data g
-- INSERTION TESTS
PRINT('INSERT INTO CHECKSUM/HASH')
INSERT INTO #Store2(Hsh, Data)
    SELECT CHECKSUM(Data), Data FROM @Data
-- SELECTION TESTS
PRINT('SELECT FROM CHECKSUM/HASH')
SELECT TOP 5000 d.Data, (SELECT s.Id FROM #Store2 s WHERE Hsh = CHECKSUM(d.Data) AND Data = d.Data) FROM @Data d
--
SET STATISTICS TIME OFF
END 

结果(简而言之)我的方法实现更快(+30%)插入“经过时间 = 7339 毫秒”与“经过时间 = 10318 毫秒”,但是,较慢(-30%)选择“经过时间 = 37 毫秒”与“经过的时间 = 28 毫秒”。

另一个有趣的注意事项是您不能“正确”索引 URL VARCHAR 字段,因为长度(根据 http 规范 ~4kb)将大于 900 字节(SQL 2008 的最大允许密钥大小)。虽然 SQL 对此仅给出警告,但警告确实指出某些 INSERTS/UPDATES 可能会失败。

Warning! The maximum key length is 900 bytes. The index 'CIX_STORE1_DATA' has maximum length of 4000 bytes. For some combination of large values, the insert/update operation will fail.

我本身不是 SQL Guru,也许我的测试方法不是最准确/最有用的,但是关于不明智的用户端优化与“黑盒”相比,这个话题非常有趣。

【讨论】:

  • 我最近有类似的事情。是不是 url 上的索引可能会做一些聪明的事情,比如在内部使用哈希/校验和来加快索引速度?我问了这个问题:stackoverflow.com/questions/7954602/…,大多数回复是“不要插入哈希码/校验和,让数据库担心”。
  • stackoverflow 上有很多“让 {x} 担心它”。当我们总是依靠别人来解决我们自己可以轻松解决的问题时,我担心未来的创新和解决问题的能力。无论如何 - 为这条评论添加我的两分钱价值(不添加任何实际信息):做一个测试 - 用一个体面的样本集来双向计时。如果我们认为 SQL 在不插入哈希的情况下更快,那么 GJ Microsoft - 如果不是,那么 GJ MonsterMMORPG 你可以更快地获得你的应用程序并为你的工具带学习另一个技巧。
  • 非常感谢您的回答。现在我只抓取某些网站。所以我可以清楚地说,总的 url 数永远不会超过 1000 万。我想在 10m 范围内发生碰撞的机会非常低。这不是很关键的问题,所以我可以接受几次碰撞,因为速度更快。
  • @AdamG.Carstensen:这并不总是一个好的答案,但数据库的整个设计都经过优化,可以尽可能快地进行查找,所以对于大多数人来说,一旦你获得了你想要的索引,然后尽管人们不太可能写出更好的东西来进行优化,但我会认为这是不可能的。我可能是错的,这就是为什么我提出这个问题,而不是仅仅投反对票并宣布完全错误。你是对的,如果我们觉得这些东西是一个问题,我们应该测试这些东西,但是在我提出问题之后,我得出结论认为尝试改进 MS 的工作是浪费我的时间。
  • @Chris - 很高兴您解决了您的问题,如果我的评论似乎带有敌意,我深表歉意。这当然不是故意的。 SQL 和所有数据库的问题在于,虽然它们充满了可能会使宽颈动物窒息的功能集,但它们仍然发布新版本和更新(表明它们并不完美)。请记住,解决每个人的问题意味着做出一些牺牲。幸运的是,有时您可以通过自己的汗水和坚持来弥补其中的一些不足。
猜你喜欢
  • 2011-12-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-08
  • 1970-01-01
  • 2015-07-28
相关资源
最近更新 更多