0


为何数据库推荐将IPv4地址存储为32位整数而非字符串?

干货分享,感谢您的阅读!

在这个互联网高速发展的时代,IP地址就像每一个网站的身份证,帮助我们在浩瀚的网络海洋中找到方向。但你是否想过,这些数字串究竟是如何被存储的?想象一下,如果把你的家地址写成一串长长的数字,或者把它变成一个32位的整数,这听起来像是科学家在做数学实验,但其实,这却是程序员们为提升数据库性能而进行的“魔法”。

就像在一家餐馆点菜,你可以选择一份诱人的意面(字符串存储),看起来色香味俱全,但吃完后发现胃部撑得慌;或者选择一份精简的沙拉(整数存储),虽然简单但却让你轻松自在,身心舒畅。那么,究竟哪个选择更胜一筹呢?在接下来的内容中,我们将一探究竟,揭秘IP地址存储的“幕后故事”,让你不再是一个无知的网络游民,而是一位懂得选择的智者!准备好了吗?让我们一起踏上这段探索之旅吧!

一、IPv4地址在数据库中的存储方式?

在Java开发过程中,我们通常会使用数据库来存储各种类型的数据,包括IPv4地址。但是,我们应该如何存储这些IP地址才能最大程度地提高系统的性能呢?使用字符串存储IPv4地址可能会更直观,但是否是最佳选择呢?

究竟应该如何存储IPv4地址才能最大程度地提高数据库的性能和系统的效率呢?我们进行探讨下为何数据库推荐将IPv4地址存储为32位整数而非字符串,并从Java开发者的角度分析这种存储方式对系统性能和开发流程的影响。

二、IPv4地址的存储方式比较

(一)字符串存储 vs 整数存储

存储方式优点缺点字符串存储
直观性:更易于人类理解和查看

灵活性:可以存储不同格式的IPv4地址

存储空间消耗大:每个IPv4地址需要较大的存储空间

效率低下:字符串比较通常耗时较长

难以进行数学运算和比较:需要额外的格式转换和解析
整数存储
存储空间效率高:只需较小存储空间

查询效率高:整数比较速度更快,索引和查询效率更高

数学运算方便:可以直接进行数学运算和比较

可能丢失部分信息:无法存储IPv6地址等其他地址格式

不直观:整数表示的IPv4地址不易于人类理解和查看

(二)IPv4地址"192.168.1.8"说明

当我们将IPv4地址"192.168.1.8"存储为字符串时,以十进制格式显示,类似于我们平时在浏览器中看到的网址,这种表示方法直观而易读。但一般每个IPv4地址都需要15个字符的存储空间(包括3个"."分隔符和4个三位数的表示),当数据量大的时候可能会导致:存储空间的浪费+查询和索引时效率降低。因为数据库系统在处理字符串比较时,需要逐字符比较,这可能会增加查询时间。

当我们将IPv4地址存储为32位整数时,它们会以二进制格式表示,例如:3232235776。这种表示方法可能不太直观,但整数只需要4字节的存储空间,远远小于使用字符串存储所需的空间。这意味着在存储大量IPv4地址时,整数存储方式可以显著节省存储空间,还可以提高数据库查询和索引的效率。因为整数比较速度更快,数据库可以更快地执行查询操作,并且可以更有效地利用索引。

因此,虽然字符串存储方式可能更直观和易读,但整数存储方式在存储空间效率和查询效率方面更具优势,这就是为什么数据库通常推荐将IPv4地址存储为32位整数而不是字符串的原因。

三、数据库推荐32位整数存储方式原理

整数存储IPv4地址的原理非常简单,它是基于IPv4地址的32位二进制表示的。IPv4地址由四个8位组成,每个组都可以表示为一个0到255的十进制数,或者一个0x00到0xFF的十六进制数。因此,将IPv4地址存储为32位整数实际上是将这四个8位的组合表示为一个32位的二进制数。

具体来说,如果将IPv4地址 "192.168.1.8" 转换为32位整数的表示,可以将每个IPv4地址的部分转换为二进制形式。

对于 "192.168.1.8",分别转换为二进制:

  • 192 -> 11000000
  • 168 -> 10101000
  • 1 -> 00000001
  • 8 -> 00001000

将这四个二进制数按顺序连接起来(11000000101010000000000100001000),形成一个32位的二进制数。最后,将这个32位的二进制数转换为整数,即为 IPv4 地址 "192.168.1.8" 对应的32位整数。转换后的整数值是:3232235776(以十进制表示)。

四、存储方式对系统性能的影响

(一)数据准备

使用数据库管理工具(如MySQL Workbench、phpMyAdmin等)或者命令行工具(如MySQL的命令行客户端)连接到数据库服务器。

在数据库zyf中创建新表,用于存储IPv4地址,选择将IPv4地址存储为字符串类型或整数类型:

  • 如果选择字符串类型,可以使用VARCHAR
  • 如果选择整数类型,可以使用INT

具体如下:

-- 创建表,将IPv4地址存储为字符串
CREATE TABLE ip_addresses_string (
    id INT AUTO_INCREMENT PRIMARY KEY,
    ip_address VARCHAR(15)
);

-- 创建表,将IPv4地址存储为整数
CREATE TABLE ip_addresses_integer (
    id INT AUTO_INCREMENT PRIMARY KEY,
    ip_address INT UNSIGNED
);

接着我们生成大量的IPv4地址数据。可以考虑使用随机生成器来生成一系列随机的IPv4地址,以模拟真实环境中的数据。分别向

ip_addresses_string

ip_addresses_integer

表中插入生成的IPv4地址数据。具体代码如下:

package org.zyf.javabasic.ipaddresses;

import java.util.Random;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

/**
 * @program: zyfboot-javabasic
 * @description: 简单的Java程序示例,用于生成大量IPv4地址数据并插入到数据库表中
 * @author: zhangyanfeng
 * @create: 2024-05-02 23:29
 **/
public class IPAddressDataGenerator {
    public static void main(String[] args) {
        // 数据库连接信息
        String url = "jdbc:mysql://localhost:3306/zyf";
        String username = "root";
        String password = "Zyf2014";

        // 生成大量IPv4地址数据
        int numAddresses = 10000; // 要生成的IPv4地址数量
        String[] ipAddresses = generateIPv4Addresses(numAddresses);

        // 将数据插入到数据库表中
        insertData(url, username, password, ipAddresses);
    }

    // 生成大量IPv4地址数据
    private static String[] generateIPv4Addresses(int numAddresses) {
        String[] ipAddresses = new String[numAddresses];
        Random rand = new Random();

        for (int i = 0; i < numAddresses; i++) {
            // 生成随机的IPv4地址
            String ipAddress = rand.nextInt(256) + "." + rand.nextInt(256) + "." + rand.nextInt(256) + "." + rand.nextInt(256);
            ipAddresses[i] = ipAddress;
        }

        return ipAddresses;
    }

    // 将数据插入到数据库表中
    private static void insertData(String url, String username, String password, String[] ipAddresses) {
        try (Connection connection = DriverManager.getConnection(url, username, password)) {
            // 插入字符串存储的IPv4地址数据
            insertIPv4Addresses(connection, "ip_addresses_string", ipAddresses);

            // 将IPv4地址转换为整数存储并插入数据
            long[] integerAddresses = convertToInteger(ipAddresses);
            insertIntegerAddresses(connection, "ip_addresses_integer", integerAddresses);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    // 插入字符串存储的IPv4地址数据
    private static void insertIPv4Addresses(Connection connection, String tableName, String[] ipAddresses) throws SQLException {
        String sql = "INSERT INTO " + tableName + " (ip_address) VALUES (?)";
        try (PreparedStatement statement = connection.prepareStatement(sql)) {
            for (String ipAddress : ipAddresses) {
                statement.setString(1, ipAddress);
                statement.addBatch();
            }
            statement.executeBatch();
        }
    }

    // 将IPv4地址转换为整数存储并插入数据
    private static void insertIntegerAddresses(Connection connection, String tableName, long[] integerAddresses) throws SQLException {
        String sql = "INSERT INTO " + tableName + " (ip_address) VALUES (?)";
        try (PreparedStatement statement = connection.prepareStatement(sql)) {
            for (long ipAddress : integerAddresses) {
                statement.setLong(1, ipAddress);
                statement.addBatch();
            }
            statement.executeBatch();
        }
    }

    // 将IPv4地址转换为整数存储
    private static long[] convertToInteger(String[] ipAddresses) {
        long[] integerAddresses = new long[ipAddresses.length];
        for (int i = 0; i < ipAddresses.length; i++) {
            String[] parts = ipAddresses[i].split("\\.");
            long ipAddress = 0;
            for (int j = 0; j < 4; j++) {
                ipAddress += Long.parseLong(parts[j]) << (24 - (8 * j));
            }
            integerAddresses[i] = ipAddress;
        }
        return integerAddresses;
    }
}

(二)查询效率比较

我们对这两种存储方式进行查询,并记录每次查询的时间。最后,我们比较两种存储方式的查询时间,以确定哪种方式的查询效率更高。代码如下:

package org.zyf.javabasic.ipaddresses;

import java.sql.*;

/**
 * @program: zyfboot-javabasic
 * @description: 比较两种存储方式的查询时间,以确定哪种方式的查询效率更高。
 * @author: zhangyanfeng
 * @create: 2024-05-02 23:44
 **/
public class IPAddressStorageComparison {
    public static void main(String[] args) {
        // Connect to the database
        Connection connection = null;
        try {
            connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/zyf", "root", "Zyf2014");

            // Query IPv4 addresses stored as strings
            long startTimeString = System.currentTimeMillis();
            Statement statement = connection.createStatement();
            ResultSet resultSetString = statement.executeQuery("SELECT * FROM ip_addresses_string WHERE ip_address = '192.168.1.1'");
            long endTimeString = System.currentTimeMillis();
            long durationString = endTimeString - startTimeString;
            System.out.println("Query time for string storage: " + durationString + " milliseconds");

            // Query IPv4 addresses stored as integers
            long startTimeInteger = System.currentTimeMillis();
            ResultSet resultSetInteger = statement.executeQuery("SELECT * FROM ip_addresses_integer WHERE ip_address = 3232235777");
            long endTimeInteger = System.currentTimeMillis();
            long durationInteger = endTimeInteger - startTimeInteger;
            System.out.println("Query time for integer storage: " + durationInteger + " milliseconds");

            // Close the connection
            connection.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

具体运行如下:

根据查询时间,可以看出整数存储的查询时间明显短于字符串存储的查询时间。整数存储的查询时间为4毫秒,而字符串存储的查询时间为19毫秒。这表明在这次测试中,整数存储的查询效率要比字符串存储的查询效率高。

(三)索引性能比较

在数据库表中分别为IPv4地址字段创建索引,具体如下:

-- 为字符串存储的IPv4地址字段创建索引
CREATE INDEX idx_ip_address_string ON ip_addresses_string (ip_address);

-- 为整数存储的IPv4地址字段创建索引
CREATE INDEX idx_ip_address_integer ON ip_addresses_integer (ip_address);

这个时候在运行上面的代码,输出如下:

根据查询时间结果,可以看出整数存储的索引查询时间明显短于字符串存储的索引查询时间,整数存储的索引查询时间为1毫秒,而字符串存储的索引查询时间为10毫秒。这表明在这次测试中,整数存储的索引查询效率要比字符串存储的索引查询效率更高。

五、应用层IP转换操作

提供一个

IPUtils

工具类用于IPv4地址和长整型数值之间的相互转换。通过位运算和位掩码,避免了字符串操作和对象创建,提高转换过程的执行效率:

package org.zyf.javabasic.ipaddresses;

/**
 * @program: zyfboot-javabasic
 * @description: 使用位运算和位掩码来进行IPv4地址和长整型数值的转换
 * @author: zhangyanfeng
 * @create: 2024-05-03 00:08
 **/
public class IPUtils {
    public static long ipToLong(String ip) {
        String[] parts = ip.split("\\.");
        return (Long.parseLong(parts[0]) << 24) +
                (Long.parseLong(parts[1]) << 16) +
                (Long.parseLong(parts[2]) << 8) +
                Long.parseLong(parts[3]);
    }

    public static String longToIp(long longIp) {
        StringBuilder sb = new StringBuilder();
        sb.append((longIp >>> 24) & 0xFF).append(".");
        sb.append((longIp >>> 16) & 0xFF).append(".");
        sb.append((longIp >>> 8) & 0xFF).append(".");
        sb.append(longIp & 0xFF);
        return sb.toString();
    }

    public static void main(String[] args) {
        String ip1 = "10.122.28.76";
        long ip1ToLong = ipToLong(ip1);
        System.out.println("IPv4地址 \"" + ip1 + "\" 转换为长整型数值的结果是:" + ip1ToLong);

        String ip2 = "10.168.0.45";
        long ip2ToLong = ipToLong(ip2);
        System.out.println("IPv4地址 \"" + ip2 + "\" 转换为长整型数值的结果是:" + ip2ToLong);

        long longIp = 197958752L;
        String longIpToIp = longToIp(longIp);
        System.out.println("长整型数值 \"" + longIp + "\" 转换为IPv4地址的结果是:" + longIpToIp);
    }
}

六、总结

探讨在Java开发中存储IPv4地址的最佳方式。通过对比字符串存储和整数存储两种方式的优缺点,我们发现整数存储方式在存储空间效率、查询效率和数学运算方面更具优势。虽然字符串存储方式更直观易读,但在处理大量数据时会浪费存储空间并降低查询效率。

通过具体的示例代码演示了如何生成大量的IPv4地址数据,并将其插入到数据库表中。通过查询和索引效率的比较,我们验证了整数存储方式在性能方面的优势。


本文转载自: https://blog.csdn.net/xiaofeng10330111/article/details/138402121
版权归原作者 张彦峰ZYF 所有, 如有侵权,请联系我们删除。

“为何数据库推荐将IPv4地址存储为32位整数而非字符串?”的评论:

还没有评论