受 stackoverflow 上以下两个答案的启发,我尝试实现一个表,目标是在其中存储 User-Agent 字符串:
这是我的表结构:
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)
);
我想实现以下目标:
输入:User-Agent 字符串,只有当它不存在时才应该插入到表中
输出: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'));
- 是否可以从这两个查询中进行一个查询?
- 如何在速度和优雅方面改进表结构或查询?
最佳答案
最重要的是摆脱INSERT IGNORE
。我发现即使失败也会增加主键。您可以通过这种方式快速烧掉 40 亿个 key 。首先执行 SELECT
,这将是最常见的情况。
我的第一个想法是将逻辑放入数据库函数中。这为您提供了封装的所有好处。然后您可以稍后更改它的工作方式。
我的第二个目标是摆脱那个散列。它有效地取代了 ua 上的索引。由于您只需要等价检查来提高性能,因此哈希索引是理想的选择,但大多数 MySQL 表格式不支持这些索引。
我会在用户代理的前 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 就像在说“尽管有更好的锁,但我还是会使用这把破锁,因为我认为这扇门现在不重要”。您不是安全专家(我也不是),不知道哪些部分重要哪些不重要。只需对所有内容进行适当的锁定即可。如果 SHA1 被证明是一些巨大的性能问题,您可以随时更改它,这要归功于函数的封装。
无论基准测试结果如何,我敢打赌,分析将揭示您的选择对它所属的任何系统的性能没有影响。使用更简单、更灵活的索引选项,如果以后出现问题,请对其进行优化。
关于mysql - 将 User-Agent 字符串的哈希存储在 MySQL 表中 : insert if not exists, 返回 id,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29088515/