将 User-Agent 字符串的哈希存储在 MySQL table 中:如果不存在则插入,return id
Store hashes of User-Agent strings in a MySQL table: insert if not exists, return id
受Whosebug上以下两个答案的启发,我尝试实现一个table,目的是在其中存储User-Agent字符串:
这是我的 table 结构:
CREATE TABLE IF NOT EXISTS ua_strings (
ua_id INTEGER PRIMARY KEY AUTO_INCREMENT,
ua_hash BINARY(16),
ua TEXT,
UNIQUE KEY ua_hash (ua_hash)
);
我想实现以下目标:
输入:仅当table不存在时才应插入的用户代理字符串
输出:ua_id
到目前为止我想出了这个解决方案:
INSERT IGNORE INTO ua_strings (ua_hash, ua) VALUES (UNHEX(MD5('test')), 'test');
SELECT ua_id FROM ua_strings WHERE ua_hash = UNHEX(MD5('test'));
- 是否可以从这两个查询中进行一个查询?
- 如何在速度和优雅方面改进我的 table 结构或查询?
最重要的是摆脱INSERT IGNORE
。我发现即使失败也会增加主键。您可以通过这种方式快速烧掉 40 亿个密钥。先做SELECT
,无论如何这将是最常见的情况。
我的第一个想法是将逻辑放入数据库函数中。这为您提供了封装的所有好处。然后您可以稍后更改它的工作方式。
我的第二个目标是摆脱那个散列。它有效地取代了 ua 上的索引。由于您只需要等价性检查就可以提高性能,因此哈希索引是理想的选择,但大多数 MySQL table 格式不支持这些索引。
我会在用户代理的前 255 个字节上使用索引,这应该足以让 MySQL 完成它的工作。如果您需要做的不仅仅是简单的获取,这还可以为您提供完整索引的好处。
CREATE TABLE IF NOT EXISTS ua_strings (
ua_id INTEGER UNSIGNED PRIMARY KEY AUTO_INCREMENT,
ua TEXT,
KEY(ua(255))
);
函数看起来像这样(注意,我不是最擅长编写 MySQL 函数的人)。
DELIMITER //
CREATE FUNCTION get_ua_id (ua_string TEXT)
RETURNS INTEGER
BEGIN
DECLARE ret INTEGER;
SELECT ua_id INTO ret FROM ua_strings WHERE ua = ua_string;
/* It's not in the table, put it in the table */
CASE WHEN ROW_COUNT() = 0 THEN
INSERT INTO ua_strings (ua) VALUES (ua_string);
SELECT LAST_INSERT_ID() INTO ret;
ELSE BEGIN END;
END CASE;
RETURN ret;
END//
DELIMITER ;
具有散列的函数看起来非常相似。隐藏函数中的实现细节并对两者进行基准测试。
并且真的不要使用 MD5。使用 SHA1 不会影响性能,您可以为每个条目留出额外的 4 个字节,这将避免隐藏的问题。使用 MD5 就像说 "Even though there's locks better in every way, I'll use this crappy lock because I don't think this door is important right now"。您不是安全专家(我也不是),不知道哪些部分重要哪些不重要。只需对所有内容进行适当的锁定即可。如果 SHA1 被证明是一些巨大的性能问题,由于函数的封装,您可以随时更改它。
无论基准测试结果如何,我敢打赌,分析将揭示您的选择对它所属的任何系统的性能没有影响。使用更简单、更灵活的索引选项,如果以后出现问题,请对其进行优化。
受Whosebug上以下两个答案的启发,我尝试实现一个table,目的是在其中存储User-Agent字符串:
这是我的 table 结构:
CREATE TABLE IF NOT EXISTS ua_strings (
ua_id INTEGER PRIMARY KEY AUTO_INCREMENT,
ua_hash BINARY(16),
ua TEXT,
UNIQUE KEY ua_hash (ua_hash)
);
我想实现以下目标:
输入:仅当table不存在时才应插入的用户代理字符串
输出:ua_id
到目前为止我想出了这个解决方案:
INSERT IGNORE INTO ua_strings (ua_hash, ua) VALUES (UNHEX(MD5('test')), 'test');
SELECT ua_id FROM ua_strings WHERE ua_hash = UNHEX(MD5('test'));
- 是否可以从这两个查询中进行一个查询?
- 如何在速度和优雅方面改进我的 table 结构或查询?
最重要的是摆脱INSERT IGNORE
。我发现即使失败也会增加主键。您可以通过这种方式快速烧掉 40 亿个密钥。先做SELECT
,无论如何这将是最常见的情况。
我的第一个想法是将逻辑放入数据库函数中。这为您提供了封装的所有好处。然后您可以稍后更改它的工作方式。
我的第二个目标是摆脱那个散列。它有效地取代了 ua 上的索引。由于您只需要等价性检查就可以提高性能,因此哈希索引是理想的选择,但大多数 MySQL table 格式不支持这些索引。
我会在用户代理的前 255 个字节上使用索引,这应该足以让 MySQL 完成它的工作。如果您需要做的不仅仅是简单的获取,这还可以为您提供完整索引的好处。
CREATE TABLE IF NOT EXISTS ua_strings (
ua_id INTEGER UNSIGNED PRIMARY KEY AUTO_INCREMENT,
ua TEXT,
KEY(ua(255))
);
函数看起来像这样(注意,我不是最擅长编写 MySQL 函数的人)。
DELIMITER //
CREATE FUNCTION get_ua_id (ua_string TEXT)
RETURNS INTEGER
BEGIN
DECLARE ret INTEGER;
SELECT ua_id INTO ret FROM ua_strings WHERE ua = ua_string;
/* It's not in the table, put it in the table */
CASE WHEN ROW_COUNT() = 0 THEN
INSERT INTO ua_strings (ua) VALUES (ua_string);
SELECT LAST_INSERT_ID() INTO ret;
ELSE BEGIN END;
END CASE;
RETURN ret;
END//
DELIMITER ;
具有散列的函数看起来非常相似。隐藏函数中的实现细节并对两者进行基准测试。
并且真的不要使用 MD5。使用 SHA1 不会影响性能,您可以为每个条目留出额外的 4 个字节,这将避免隐藏的问题。使用 MD5 就像说 "Even though there's locks better in every way, I'll use this crappy lock because I don't think this door is important right now"。您不是安全专家(我也不是),不知道哪些部分重要哪些不重要。只需对所有内容进行适当的锁定即可。如果 SHA1 被证明是一些巨大的性能问题,由于函数的封装,您可以随时更改它。
无论基准测试结果如何,我敢打赌,分析将揭示您的选择对它所属的任何系统的性能没有影响。使用更简单、更灵活的索引选项,如果以后出现问题,请对其进行优化。