3707307条数据优化
背景:单表数据目前370万,主要就是存储的IPV4,如 ip_begin(int)、ip_end(int)、经度(longitude)、纬度(latitude)、city_code(城市编码)等,将ip使用int存储是因为占用的数据量小,在4个字节,如果选用字符串增大了表中存储数据压力,另外大数量使用字符串性能相当低效。
需求:接收前端传递过来的一个ip(字符串类型),根据这个ip查询在ip_begin与ip_end 范围内的数据,其实也就是一条数据而已,开始觉得这个需求很简单,一个查询sql就完事,事实上并不是如此,
select * from emp_ip where inet_aton(‘ip’)>ip_begin and inet_aton(‘ip’)<ip_end。在Navicat中执行显示用了3.8s,再次执行 3.7s,在执行 3.8…,这样速度肯定不行,怎么办,优化呗。既然用到了ip_begin和ip_end这两个字段,那么给他们两个加上普通索引,再次执行,好像好了 0.6s,再次执行0.5s。目前看来是可以的,此时换了一个ip试了一下,又变成了3.8s, 难道查找范围还和ip地址有关系???答案是有关系的,如果ip是2xx开头的执行的就会很快,但是如果ip是1xx开头速度就会很慢,至于为什么这样,请教了大佬 也没有闹出个所以然…。
优化还是继续,此时想到了项目启动初始化一次性加载表中所有的数据,然后存放在list中,接着遍历list,拿着ip和list集合中的ip_begin和ip_end依次比较,只到找到为止。
优点:速度大大提升,不管什么样的ip,一次请求都会在零点几秒内完成。
缺点:内存中会有一个超大的对象存在,占用了大量的资源,非必要不可取。
方式二:使用平衡二叉树节点,判断ip是否大于ip_begin,如果大于放在左边,如果小于就放在右边,依次类推。。。
方式三:在原有表中增加一个扩展字段,仔细分析发现,不管是ip_begin还是ip_end 他们都是有规律的比如192.168.0.0~192.168.255.255,ip_begin和ip_end开头都是一样的,变化的是后边的,那么我扩展字段就存储这个198,并且加上索引,此时sql变成了这样 select * from emp_ip where ret=198 and inet_aton(‘ip’)>ip_begin and inet_aton(‘ip’)<ip_end,(先去筛选ret=198的,然后在ip_begin和ip_end范围内查询)此时无论怎么变化ip,查询速度都会在0.4s内完成,大大提高了查询效率。
其实还有个备用方案:就是分表,吧这370万条数据均匀分成十张表,单表37万,这样查询的速度也是很快的。