如何在 MySQL 的单列中存储 128 位数字?
Posted
技术标签:
【中文标题】如何在 MySQL 的单列中存储 128 位数字?【英文标题】:How to store a 128 bit number in a single column in MySQL? 【发布时间】:2011-05-25 14:12:20 【问题描述】:我正在更改一些表格以将 IP 地址存储为数字而不是字符串。这对于 IPv4 很简单,其中 32 位地址可以放入整数列中。但是,IPv6 地址是 128 位的。
mysql documentation 仅显示最多 64 位(“bigint”)的数字类型。
对于 IPv6,我应该坚持使用 char/varchar 吗? (理想情况下,我希望对 IPv4 和 IPv6 使用同一列,所以我不想这样做)。
还有什么比使用两个 bigint 列更好的吗?我宁愿在使用地址时不必将值分成上下 /64。
我正在使用 MariaDB 5.1 - 如果在更高版本的 MySQL 中有更好的解决方案,那么很高兴知道,尽管不是立即有用。
[EDIT]请注意,我正在寻求最佳方法的推荐 - 显然有多种方法可以做到这一点(包括现有的字符串表示),但哪个(就性能而言)最好? (即,如果有人已经完成了分析,那将节省我的时间,或者如果我遗漏了一些明显的东西,那也很高兴知道)。
【问题讨论】:
Size for storing IPv4, IPv6 addresses as a string 或 How to store IPv6-compatible address in a relational database 的可能重复项 ***.com/questions/3455320/… 的答案之一在这里是相关的,但问题根本不是问同样的事情(实际上是相反的问题)。 ***.com/questions/420680/… 但是看起来确实是同一个问题。谢谢你-我确实搜索过,但没有找到。我不反对将其作为欺骗而关闭。 我真的先搜索了,老实说!但这也是***.com/questions/1120371/… 的欺骗。 我正在做一些基准测试来解决这个问题,因为似乎没有任何明确的通用答案。到目前为止,任何存储方法之间的性能差异似乎都非常小(尽管我的基准测试中可能有错误......)。完成后我会更新结果。 在处理 IPv6 时,我总是只存储和使用前缀,即前 64 位,因为后缀经常更改并且可以随意更改。 【参考方案1】:我发现自己在问这个问题,并且从我阅读的所有帖子中都没有发现任何性能比较。所以这是我的尝试。
我创建了以下表格,其中填充了来自 100 个随机网络的 2,000,000 个随机 IP 地址。
CREATE TABLE ipv6_address_binary (
id SERIAL NOT NULL AUTO_INCREMENT PRIMARY KEY,
addr BINARY(16) NOT NULL UNIQUE
);
CREATE TABLE ipv6_address_twobigints (
id SERIAL NOT NULL AUTO_INCREMENT PRIMARY KEY,
haddr BIGINT UNSIGNED NOT NULL,
laddr BIGINT UNSIGNED NOT NULL,
UNIQUE uidx (haddr, laddr)
);
CREATE TABLE ipv6_address_decimal (
id SERIAL NOT NULL AUTO_INCREMENT PRIMARY KEY,
addr DECIMAL(39,0) NOT NULL UNIQUE
);
然后我选择每个网络的所有 ip 地址并记录响应时间。 twobigints 表的平均响应时间约为 1 秒,而二进制表的平均响应时间约为百分之一秒。
这里是查询。
注意:
X_[HIGH/LOW] 是 X 的最高/最低有效 64 位
当 NETMASK_LOW 为 0 时,AND 条件被省略,因为它总是产生 true。对性能影响不大。
SELECT COUNT(*) FROM ipv6_address_twobigints
WHERE haddr & NETMASK_HIGH = NETWORK_HIGH
AND laddr & NETMASK_LOW = NETWORK_LOW
SELECT COUNT(*) FROM ipv6_address_binary
WHERE addr >= NETWORK
AND addr <= BROADCAST
SELECT COUNT(*) FROM ipv6_address_decimal
WHERE addr >= NETWORK
AND addr <= BROADCAST
平均响应时间:
图表:
BINARY_InnoDB 0.0119529819489
BINARY_MyISAM 0.0139244818687
DECIMAL_InnoDB 0.017379629612
DECIMAL_MyISAM 0.0179929423332
BIGINT_InnoDB 0.782350552082
BIGINT_MyISAM 1.07809265852
【讨论】:
好帖子!做范围比较之类的事情会很有趣,因为这似乎是将地址存储为数字的主要用例。【参考方案2】:我一直使用一个字符串或两个 64 位整数。前者是我只想记录的情况,后者是我需要计算某个地址是否包含在某个网络中,甚至两个网络是否重叠的情况。
当将其存储为整数时,唯一的选择确实是将其拆分为两个 64 位数字。由于这使比较变得更加麻烦,除非您需要数值计算,以查看 IP 是否属于某个网络,否则我不会这样做。
我不会太担心将 IPv6 地址存储在字符串中的性能 - 取决于您对数据执行的查找次数。通常,数据很少,或者数据很少。是的,存储和查找的效率不如数字,但并不比存储电子邮件地址、人名或用户名更痛苦。
为什么不能在字符串字段中混合使用 IPv4 和 IPv6?检索时很容易区分它们。它们的可能值范围不重叠。
简而言之:使用数字检查重叠,在其他地方使用字符串。与易用性相比,字符串的低效无关紧要。
【讨论】:
【参考方案3】:引用:“你考虑过二进制 (64)”
Storing very large integers in MySQL
【讨论】:
你有关于二进制(64)与两个大整数或小数的效率的任何信息吗?我可以很容易地看到几种存储数据的方法,但我想知道哪种方法是最好的。 我会说“最佳”方式很大程度上取决于您在存储之前和读回之后如何处理这些数字。例如,为了代码的有用性,而不是这一切都是痛苦的。既然您提到了性能,如果您期望速度很快,字符串转换可能会很慢......但仍然比从数据库读取快一个数量级(或两个)。 注意——128位是“二进制(16)”——你用字节表示大小。以上是关于如何在 MySQL 的单列中存储 128 位数字?的主要内容,如果未能解决你的问题,请参考以下文章