IP地址定位区间的问题分析

Wesley13
• 阅读 438

   以前写过一篇Oracle中关于IP地址定位的问题分析,最后引申出了一系列的问题。当时问题紧急严峻,抓取了10053事件定位源头,想出了一个解决妙法,还自鸣得意了下,结果忙活完之后看看行业里的解决方案都大体如此,我的心凉了半截。

   我总是希望找到一些与众不同的点来解读这一类问题,结果在偶然的一天从MySQL这里找到了一些思路。

    我先来分析下之前问题和一些收获。

    需求是输入一个IP,能够根据IP从一个数据字典表里查询IP区段,返回IP对应的区域,这就是一个看起来很简单的IP地址定位的问题。

   从系统负载方面,CPU的负载较高,而其中很大的一方面代价就是IP地址和数字(IP地址转换为数字)之间的转换和映射。

   Buffer Gets指标极高,这个部分其实和整个语句的查取效果有关,如果没有找到匹配的数据,就会扫描更多的块。这个部分一个立竿见影的效果就是使用rownum的方式来截断,在这个基础上,和Oracle的朋友聊,其实也有一些改进措施的,这个部分对于极限优化来说可以参考,所以暂且放一放。

  从索引的角度来考虑,Range Scan的方式总是会有优点和缺点,不可能把它同时结合起来达到一个最优的效果,换做那一个数据库都是如此,只能说有些回表的数据处理Oracle隐式(比如使用rowid))做好了,而MySQL里面可能需要单独处理。

   问题就交代到这里,我今天想再次讨论这个问题是想从几个基础的问题开始来聊聊MySQL在这方面的优势,没错,是相比于Oracle的优势的地方。

    首先我们来说说表结构的设计,如果在Oracle里面,当时设计的地址信息如下:

`COLUMN_ID COLUMN_NAME                    DATA_TYPE       DATA_LENGTH NULLABLE


         1 IP_ID                          NUMBER(10,0)             22 N
         2 IP_LEFT_LINE                   VARCHAR2(15)             15 N
         3 IP_RIGHT_LINE                  VARCHAR2(15)             15 N
         4 IP2NUM_LEFT_LINE               NUMBER(10,0)             22 N
         5 IP2NUM_RIGHT_LINE              NUMBER(10,0)             22 N
         6 COUNTRY                        VARCHAR2(20)             20 Y
         7 PROVINCE                       VARCHAR2(20)             20 Y
         8 CAPITAL                        VARCHAR2(20)             20 Y`

里面对IP地址和IP地址转换后的数字都做了持久化,查询的逻辑相对就比较别扭了。

比如下面:B1是传入的IP地址,即一个字符串,会先转换为数字,然后做Range Scan。

SELECT IP_ID,COUNTRY,PROVINCE,CAPITAL FROM SWD_IP2COUNTY WHERE STRIPTOINT(:B1 ) BETWEEN IP2NUM_LEFT_LINE AND IP2NUM_RIGHT_LINE

如果换做MySQL,有哪些点需要考虑呢。

第一个考虑点还是数据类型,IP地址是一个字符串,我们是考虑使用varchar类型还是char呢。

  假设一个IP地址为10.127.133.199,字符串的长度就是14位,最高设置为3*4+3=15位,这是第一点。

  而如果我们存储了一个IP,则意味着这个工作还没有完成,我们还需要转换,所以还不如直接转换为数值,所以综合起来,其实我们实现这个需求,从简化的角度来看,其实不需要一个字符型,而是需要一个数值型即可。

   那么问题来了,数值型数据类型其实是很丰富的,这一点和Oracle大大不同,Oracle里面很多开发,DBA都懒了,或者说Oracle内部已经做好了这种适配,数值精度也不需要更多考虑了,长度也不需要区别对待了,直接一个number类型,想调精度,就直接在这个基础上改,比如number(10,3),可以定义长度和精度。MySQL在这方面就分得比较轻,有支持0-128以内的tiny int,32767的smallint等,每一个数据类型都抠的很细。

   所以在Oracle里面的豪气在这里就是粗放了,一定需要认真区别对待。

   因为我们打算使用数值类型,最后我们选择了int(11),没有留出很富余的值是因为我们从设计的角度来考虑尽可能按需分配。

> create table ip_range(ip int(11) ); Query OK, 0 rows affected (0.01 sec)

我们插入两行值:

> insert into ip_range values(inet_aton('127.0.0.1')),(inet_aton('192.168.1.1')); ERROR 1264 (22003): Out of range value for column 'ip' at row 2结果发现竟然溢出了,SQL_Mode是严格模式。

好吧,看来我们太过于乐观了。逐个击破。

> insert into ip_range values(inet_aton('127.0.0.1'));

Query OK, 1 row affected (0.00 sec)

原来是这里的问题:

> insert into ip_range values(inet_aton('192.168.1.1')); ERROR 1264 (22003): Out of range value for column 'ip' at row 1

这是因为int的数值类型其实分为有符号和无符号两种,区间分别是2147483647和4294967295,所以IP地址的需求我们只需要考虑无符号的情况,修改字段类型。

> alter table ip_range modify ip int(11) unsigned; Query OK, 1 row affected (0.02 sec) Records: 1  Duplicates: 0  Warnings: 0然后再次插入就没有问题了。

> insert into ip_range values(inet_aton('192.168.1.1')); Query OK, 1 row affected (0.00 sec)这里需要提一下,就是对于IP地址的转换,MySQL已经提供了这个转换的方法,可以互相转换。分别是inet_ntoa(数值转为IP),inete_aton(IP转为数值)

> select (inet_ntoa(ip)) from ip_range; +-----------------+ | (inet_ntoa(ip)) | +-----------------+ | 127.0.0.1       | | 192.168.1.1     | +-----------------+ 2 rows in set (0.00 sec) 有了这些铺垫,结合索引信息,实现这个需求问题 不大。

IP地址定位区间的问题分析

本文分享自微信公众号 - 杨建荣的学习笔记(jianrong-notes)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Stella981 Stella981
2年前
JS 苹果手机日期显示NaN问题
问题描述newDate("2019122910:30:00")在IOS下显示为NaN原因分析带的日期IOS下存在兼容问题解决方法字符串替换letdateStr"2019122910:30:00";datedateStr.repl
Stella981 Stella981
2年前
SpringBoot整合Redis乱码原因及解决方案
问题描述:springboot使用springdataredis存储数据时乱码rediskey/value出现\\xAC\\xED\\x00\\x05t\\x00\\x05问题分析:查看RedisTemplate类!(https://oscimg.oschina.net/oscnet/0a85565fa
Easter79 Easter79
2年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Easter79 Easter79
2年前
SpringBoot整合Redis乱码原因及解决方案
问题描述:springboot使用springdataredis存储数据时乱码rediskey/value出现\\xAC\\xED\\x00\\x05t\\x00\\x05问题分析:查看RedisTemplate类!(https://oscimg.oschina.net/oscnet/0a85565fa
Stella981 Stella981
2年前
RobotFramework自动化测试之元素定位
前言:最近在做基于RF框架的Web自动化测试,其中涉及到元素的定位,主要用到id、name、xpath、css四中定位方法,尤其后面的两种方法特别有效,可以解决大部分的定位问题。
Wesley13 Wesley13
2年前
Java日期时间API系列23
  有时候,往往需要统计某个时间区间的销量等问题,这就需要准确的起始时间,获取准确开始时间00:00:00,获取准确结束时间23:59:59。下面增加了一一些方法,获取当天起始时间,昨天起始时间,当前月第一天开始时间,当前月最后一天结束时间,上个月第一天开始时间,上个月最后一天结束时间,某个指定月的起始结束时间等等。其中月份最后一天往往因为月份不同和
Wesley13 Wesley13
2年前
35岁是技术人的天花板吗?
35岁是技术人的天花板吗?我非常不认同“35岁现象”,人类没有那么脆弱,人类的智力不会说是35岁之后就停止发展,更不是说35岁之后就没有机会了。马云35岁还在教书,任正非35岁还在工厂上班。为什么技术人员到35岁就应该退役了呢?所以35岁根本就不是一个问题,我今年已经37岁了,我发现我才刚刚找到自己的节奏,刚刚上路。
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这