got*_*tqn 12 database-design sql-server sql-server-2012 sql-clr nonclustered-index
我将创建一个包含两个字段的表 - IDasBIGINT和IPAddressasvarchar(45)或者varbinary(16)。这个想法是存储所有唯一的 IP 地址并使用引用ID而不是IP address其他表中的实际地址。
通常,我将创建一个存储过程,该过程返回ID给定的IP address或(如果未找到地址)插入地址并返回生成的ID.
我期望有很多记录(我无法确切说出有多少),但我需要尽快执行上面的存储过程。所以,我想知道如何以文本或字节格式存储实际的 IP 地址。哪个会更好?
我已经编写了SQL CLR用于将 IP 地址字节转换为字符串和反向转换的函数,因此转换不是问题(同时使用IPv4和IPv6)。
我想我需要创建一个索引来优化搜索,但我不确定我应该将该IP address字段包含在聚集索引中,还是创建一个单独的索引以及使用哪种类型的搜索会更快?
Sol*_*zky 13
如何存储实际 IP 地址 - 以文本或字节格式。哪个会更好?
由于这里的“文本”是指VARCHAR(45)而“字节”是指VARBINARY(16),我会说:两者都不是。
鉴于以下信息(来自关于 IPv6 的维基百科文章):
地址表示
IPv6 地址的 128 位表示为 8 组,每组 16 位。每个组都写为 4 个十六进制数字,组之间用冒号 (:) 分隔。地址 2001:0db8:0000:0000:0000:ff00:0042:8329 就是这种表示的一个例子。为方便起见,在可能的情况下,可以通过应用以下规则将 IPv6 地址缩写为较短的符号。
- 从任何一组十六进制数字中删除一个或多个前导零;这通常对所有前导零或没有前导零进行。例如,组 0042 转换为 42。
- 连续的零部分被替换为双冒号 (::)。双冒号在一个地址中只能使用一次,因为多次使用会使地址不确定。RFC 5952 建议不得使用双冒号来表示省略的单个零部分。 [41]
这些规则的应用示例:
初始地址:2001:0db8:0000:0000:0000:ff00:0042:8329
去除每组中所有前导零后:2001:db8:0:0:0:ff00:42:8329
省略连续部分零后:2001 :db8::ff00:42:8329
我将首先使用 8 个VARBINARY(2)字段来表示 8 个组。组 5 - 8 的字段应该是NULL因为它们将仅用于 IPv6 地址。组 1 - 4 的字段应该NOT NULL与它们将用于 IPv4 和 IPv6 地址一样。
通过保持每个组独立(而不是将它们组合到一个VARCHAR(45)或一个VARBINARY(16)甚至两个BIGINT字段中),您可以获得两个主要好处:
IF/ IIF/CASE语句来促成。ROW COMPRESSION或 ,您将在 IPv6 地址上节省大量空间PAGE COMPRESSION。由于这两种类型的 COMPRESSION 都允许0x00占用 0 字节的字段,因此所有这些零组现在不会花费您任何费用。另一方面,如果您存储了上面的示例地址(在 Wikipedia 引用中),那么中间的 3 组全零将占用它们的全部空间(除非您这样做VARCHAR(45)并使用简化的符号,但这可能不适用于索引,并且需要特殊解析才能将其重建为完整格式,因此我们假设这不是一个选项;-)。如果您需要捕获网络,请TINYINT为该字段创建一个字段,嗯,[Network]:-)
有关网络值的更多信息,请参阅维基百科另一篇关于 IPv6 地址的文章:
网络
IPv6 网络使用的地址块是大小为 2 的幂的连续 IPv6 地址组。对于给定网络中的所有主机,地址的前导位集是相同的,称为网络地址或路由前缀。
网络地址范围以 CIDR 表示法编写。网络由块中的第一个地址(以全零结尾)、一个斜杠 (/) 和一个十进制值表示,该值等于前缀的位大小。例如,写成 2001:db8:1234::/48 的网络从地址 2001:db8:1234:0000:0000:0000:0000:0000 开始,到 2001:db8:1234:ffff:ffff:ffff:ffff 结束:ffff。
接口地址的路由前缀可以直接用CIDR表示法用地址表示。例如,地址为 2001:db8:a::123 的接口连接到子网 2001:db8:a::/64 的配置写为 2001:db8:a::123/64。
对于索引,我会说在 8 个组字段上创建一个非聚集索引,如果您决定包含它,可能还有网络字段。
最终结果应类似于以下内容:
CREATE TABLE [IPAddress]
(
IPAddressID INT NOT NULL IDENTITY(-2147483648, 1),
Group8 VARBINARY(2) NULL, -- IPv6 only, NULL for IPv4
Group7 VARBINARY(2) NULL, -- IPv6 only, NULL for IPv4
Group6 VARBINARY(2) NULL, -- IPv6 only, NULL for IPv4
Group5 VARBINARY(2) NULL, -- IPv6 only, NULL for IPv4
Group4 VARBINARY(2) NOT NULL, -- both
Group3 VARBINARY(2) NOT NULL, -- both
Group2 VARBINARY(2) NOT NULL, -- both
Group1 VARBINARY(2) NOT NULL, -- both
Network TINYINT NULL
);
ALTER TABLE [IPAddress]
ADD CONSTRAINT [PK_IPAddress]
PRIMARY KEY CLUSTERED
(IPAddressID ASC)
WITH (FILLFACTOR = 100, DATA_COMPRESSION = PAGE);
CREATE NONCLUSTERED INDEX [IX_IPAddress_Groups]
ON [IPAddress] (Group1 ASC, Group2 ASC, Group3 ASC, Group4 ASC,
Group5 ASC, Group6 ASC, Group7 ASC, Group8 ASC, Network ASC)
WITH (FILLFACTOR = 100, DATA_COMPRESSION = PAGE);
Run Code Online (Sandbox Code Playgroud)
笔记:
BIGINT用于 ID 字段,但您真的希望捕获超过 4,294,967,295 个唯一值吗?如果是这样,那么只需将字段更改为 BIGINT,然后您甚至可以将种子值更改为 0。但否则您最好使用 INT 并从最小值开始,以便您可以使用该数据类型的整个范围.SELECT *会在预期的顺序返回的字段。但是索引使它们从 1上升到 8,因为它们就是这样填写的。以文本形式表示值的计算列的示例(未完成)是:
ALTER TABLE [IPAddress]
ADD TextAddress AS (
IIF([Group8] IS NULL,
-- IPv4
CONCAT(CONVERT(TINYINT, [Group4]), '.', CONVERT(TINYINT, [Group3]), '.',
CONVERT(TINYINT, [Group2]), '.', CONVERT(TINYINT, [Group1]),
IIF([Network] IS NOT NULL, CONCAT('/', [Network]), '')),
-- IPv6
LOWER(CONCAT(
CONVERT(VARCHAR(4), [Group8], 2), ':', CONVERT(VARCHAR(4), [Group7], 2), ':',
CONVERT(VARCHAR(4), [Group6], 2), ':', CONVERT(VARCHAR(4), [Group5], 2), ':',
CONVERT(VARCHAR(4), [Group4], 2), ':', CONVERT(VARCHAR(4), [Group3], 2), ':',
CONVERT(VARCHAR(4), [Group2], 2), ':', CONVERT(VARCHAR(4), [Group1], 2),
IIF([Network] IS NOT NULL, CONCAT('/', [Network]), '')
))
) -- end of IIF
);
Run Code Online (Sandbox Code Playgroud)
测试:
INSERT INTO IPAddress VALUES (127, 0, 0, 0, 4, 22, 222, 63, NULL); -- IPv6
INSERT INTO IPAddress VALUES (27, 10, 1234, 0, 45673, 200, 1, 6363, 48); -- IPv6
INSERT INTO IPAddress VALUES (NULL, NULL, NULL, NULL, 192, 168, 2, 63, NULL); -- v4
INSERT INTO IPAddress VALUES (NULL, NULL, NULL, NULL, 192, 168, 137, 29, 16); -- v4
SELECT [IPAddressID], [Group8], [Group1], [Network], [TextAddress]
FROM IPAddress ORDER BY [IPAddressID];
Run Code Online (Sandbox Code Playgroud)
结果:
IPAddressID Group8 Group1 Network TextAddress
----------- ------ ------ ------- ---------------------
-2147483646 0x007F 0x003F NULL 007f:0000:0000:0000:0004:0016:00de:003f
-2147483645 0x001B 0x18DB 48 001b:000a:04d2:0000:b269:00c8:0001:18db/48
-2147483644 NULL 0x003F NULL 192.168.2.63
-2147483643 NULL 0x001D 16 192.168.137.29/16
Run Code Online (Sandbox Code Playgroud)越小总是会更快。使用较小的值,您可以将更多的内容放入单个页面中,从而减少 IO,可能会出现更浅的 B 树等。
当然,所有其他因素(翻译开销、可读性、兼容性、CPU 负载、索引控制能力等)都是相同的。
| 归档时间: |
|
| 查看次数: |
17893 次 |
| 最近记录: |