redis缓存原理与实现_基于Redis实现范围查询的IP库缓存设计方案

点击上方“码农沉思录”  发现更多精彩我先说下结果。我现在还不敢放线上去测,这是本地测的数据,我4g内存的电脑本地开redis,一次都没写完过全部数据,都是写一半后不是redis挂就是测试程序挂。可以肯定的是总记录数是以千万为单位的。O(log(n千万/range))的时间复杂度,本地测的结果我并不满意,7ms的时间,太久了。这个数量级的数据,就算内存缓存也很花时间,因为并不是简单的key-value,涉及到范围查找。fb004daab3b3c60e81d6fee56544f1ed.png0c685f0bc164f4bfa57a0ef00a416f2e.png使用Sorted Set实现范围查找最近系统需要更新IP库,IP库存储的是IP所属的国家和城市信息,广告主投放广告会有区域限制,所以需要根据点击广告的终端ip,获取位置信息,并判断是否满足广告投放区域的要求。最头疼的是,IP信息库是按区间存储的,拿到一个ip得要知道它所属的范围才能知道它对应哪条记录。它的表结构是这样的:Ip_from,ip_to,ccountry_code,country,regin,cityIp起始段,Ip结束段,国家编码,国家,区域(比如中国的省),城市a275d52f435f3643ae3db8a1cdf084e0.png而Ip_from和ip_to是一个数值,将ip通过公式转化后的数值。a.b.c.d
A*(256*256*256)+b*(256*256)+c*(256)+d
为了效率,程序中的转换可以写为
a*(1<<24)+b*(1<<16)+c*(1<<8)+d
在此之前我都没注意以前是怎么实现查找的,以前是用内存缓存实现,但以前的数据比较少,而查找的方式用的递归,先不说递归查找算法的缺陷。而新的ip库数据量很大,本地debug直接OOM,没有足够的内存撑起这么大的ip库。如果说,一个csv文件的大小是1g多,那么读取到jvm中,就需要4g甚至更高的内存,因为一个对象占的内存是比一行逗号隔开的字符串耗更大的内存。要实现查找算法,创建对应的数据结构,这些也会占用很大的内存。综上所述,我们考虑用redis来缓存,当然,也可以用mongodb,如果是用mongodb缓存,那就简单了。既然要用Redis,那么就不得不面对,Redis如何实现范围查询,还要支持高并发。这算是一道难题了。插入一段内容,关于如果使用Sorted Set实现范围查找,就是sql中的大于等于and小于等于。(下面是我参考的一篇博客,我觉得他的实现有些鸡肋,完全可以用一条:zadd myset 1015 1011-1015-v1 替代两条记录)。服务端对于客户端不同的版本区间会做些不同的配置,那么客户端一个版本过来怎么快速的定位是属于哪个版本区间呢?可以利用Sorted Sets的zrangebyscore命令。zadd myset 1011 v1_startzadd myset 1015 v1_end zadd myset 1018 v2_start zadd myset 1023 v2_end 如上我们像myset里插入了4条数据,代表的意思是版本区间v1是从1011-1015版本,版本区间v2是从1018-1023版本。https://www.cnblogs.com/zhanjindong/p/3549994.html那么我现在如何判断1014版本属于哪个区间呢,使用zrangebyscore如下操作: zrangebyscore myset 1014 +inf LIMIT 0 1 1)v1_end 返回v1_end说明1014属于版本区间1,上面的这个命令的意思是在myset中查找第一个score值大于等于1014的member,如果我们查找一个不在区间内的版本,比如1016: zrangebyscore myset 1014 +inf LIMIT 0 1 1)v2_starthttps://www.cnblogs.com/zhanjindong/p/3549994.html首先,我想到的是利用Redis的有序集合Sorted Set,存储每条记录的ip区间,或者叫ip范围。ip_to列作为有序集合的score。如:
zadd ip-country-city-locations-range 3756871679 3756871424~3756871679
这样就可以查询一个ip对应的score落在哪个区间,找出符合条件的第一个区间。如:
(1)将ip转为number,假设得到的值为:3756871650(2)使用zrangebyscore命令获取所在区间zrangebyscore ip-country-city-locations-range 3756871650 +inf 0 1
因为3756871650比3756871424~3756871679区间的end值3756871679小于等于,所以匹配到这条记录。但拿到这条记录后并不能说明3756871650在这个区间内,所以还要比较
3756871424<=3756871650<=3756871679
因为会存在这种情况,该区间与前一个区间并不是连续的,比如。
(1)3756870911=>3756870656~3756870911(2)3756871679=>3756871424~3756871679
明显,这两个区间之间存在断层。但可以肯定的是,如果不落在区间(2)中,也不会落在区间(1)。所以不满足就可以直接返回null了。
// [0] <= midNumber <= [1]if (midNumber.compareTo(Long.valueOf(rangeSn[0]))    || midNumber.compareTo(Long.valueOf(rangeSn[1])) > 0) {      return null;}
Ip库用hash类型存储,field取ip_from或者ip_from&ip_to,value当然就是存完整的一行记录了。最后就可以根据拿到的区间信息获取到对应记录的field,使用hget命令就能获取到最终的一行ip信息记录。
hget ip-country-city-locations 3756871424
这并不难实现,但是,耗时却非常严重,可以看下官方文档介绍的Sorted Set的zrangebyscore的时间复杂度。IP库保守估计百万条记录,如果就这样上线,百分百又是服务雪崩。改进思路:区间+Sorted Set由于Sorted Set有序集合的查询时间复杂度是O(log(n)+m),其中n是总记录数,m是此次查询获取的记录数,在limit 0 1的情况下是O(log(n)),所以一个有序集合的元素个数越多,它的查询时间耗时越长。对于一般的应用场景,如排行榜,都是只有极少数的几百条记录。而如果用于ip库的区间查询实现,记录上百万条,而且还是用于高并发场景,不把服务搞垮才怪了。既然我们要用Sorted Set,但又不能让集合的元素过大,那么我们可以分n/m个区间存储啊。假设有一百万条记录,每个Sorted Set存储1000条,那就用1000个Sorted Set集合来存储。hash的查询时间复杂度是接近O(1)的,增加1000个key在分槽位的分布式集群下根本不算什么。按照上面的思路改进后,获取一个ip的国家城市信息就变成如下步骤:1、根据ip计算出一个number值
比如:3756871650
2、根据区间大小(这一步的区间指的是每个Sorted Set的最大大小),计算出该number所在的集合的key
比如:ip-country-city-locations-range-375
3、根据这个key,去Sorted Set查询number所属的区间。
比如:zrangebyscore ip-country-city-locations-range 3756871650 +inf 0 1
5、拿到区间信息后,从区间信息获取ip范围位置信息的 field(hash类型存储)
比如查询结果区间信息为:3756871424~3756871679拿到field就是:3756871424
6、根据key和field拿到目标记录。
hget ip-country-city-locations 3756871424
编码实现我将实现封装成一个组件,目的是对外提供更简单的使用方式,封装复杂的实现逻辑,同时,内部的改动对使用者无感知。通过SPI+分层设计,利用静态代理模式等,使得组件具有极强的扩展性,如果某天想改成使用mongodb或者内存缓存,只需要实现几个接口就可以了。05682bf5cc0d8b9bed6263a98d311b16.png下面是README.MD的内容

关于数据源表的初始化

使用需要配置update启动参数:

-Dip.database.table.update=true

如:

java -Xss256k-jar -Dip.database.table.update=true xxx-1.0.0.jar

true: 首次启动就会从指定的url文件读取解析记录,插入数据表 false: 表示已经确认表存在记录了,不需要再更新。(也不会去解析文件) 默认:false解析记录与插入表是异步的,后台开启一个线程执行。耗时根据文件大小决定,我测的是86s

配置使用表

使用了java的SPI需要指定使用哪个文件解析器,也就对应使用哪种类型的表

配置redis操作实现类

使用了java的SPI如果解析配置使用了

com.chestnut.ip.database.parser.RedisIP2LocationFileParser

则需要自己实现RedisOperation,并在

com.chestnut.ip.database.suppor.IP2LocationRedisOperation

文件中配置redis操作的实现类

缓存的key

如果使用redis存储数据,则key固定为

ip-country-city-locations // 存储真实记录

ip-country-city-locations-range-* // 存储范围与真实记录的key的映射

其中ip-country-city-locations-range-后面的代表的分区索引148b54bc7f7d88277b56cf7f516f7f12.gif148b54bc7f7d88277b56cf7f516f7f12.gif201d2c2716211072051a9e4f7371cef9.gif6d08eca83671f9de4e3eef63525a8f30.pngeff3fed5623c5741f601346b4513e35d.png点个在看吧,证明你还爱我

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/500954.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mysql原生库_Mysql数据库的一些简单原生sql语句

原生sql语句查询&#xff1a;select * from 表名 &#xff1a;查找表内所有数据&#xff0c; * 代表所有where 具体条件 :where作位查询sql语句条件&#xff0c;例 select * from 表名 where 字段名指定值order by 升降序&#xff1a;与desc和asc使用,通常以int类型字段进行升…

有向图生成树是如何画的_漫画:什么是最小生成树?

作者 | 小灰来源 | 程序员小灰————— 第二天 —————————————————首先看看第一个例子&#xff0c;有下面这样一个带权图&#xff1a;它的最小生成树是什么样子呢&#xff1f;下图绿色加粗的边可以把所有顶点连接起来&#xff0c;又保证了边的权值之和最小&a…

printf 指针地址_c语言对指针的理解

先来讲一下本人学指针的经历&#xff1a;大一的时候刚接触c语言对指针这东西真的是太迷了&#xff0c;感觉麻烦难懂不想其他语言一样。但是搞懂以后就被指针的魅力吸引甚至喜欢上c语言。不多讲&#xff0c;开始&#xff01;(文章可能有些长&#xff0c;但放心全是基础的东西&am…

python 时分秒毫秒_python将时分秒转换成秒的实例

处理数据的时候遇到一个问题&#xff0c;从数据库里导出的数据是时分秒的格式&#xff1a;hh:mm:ss &#xff0c;现在我需要把它转换成秒&#xff0c;方便计算。原数据可能分两种情况&#xff0c;字段有可能是文本字符串类型的&#xff0c;也有可能是时间类型&#xff0c;他们的…

信息系统项目管理师论文优秀范文_软考 信息系统项目管理师备考指南

1&#xff0e;考试简介信息系统项目管理师考试作为全国计算机技术与软件专业技术资格&#xff08;水平&#xff09;考试&#xff08;一般简称为“软考”&#xff09;的一个高级级别&#xff0c;是从2005年开始的&#xff0c;一共考了2次&#xff0c;即2005年5月&#xff0c;200…

单片机led闪烁代码_单片机驱动LED发光二极管的电路以及编程

一、单片机驱动单个发光二极管1.电路代码:1.点亮单个LED二极管#include《reg51.h> sbit LED1P1^0&#xff1b;void main(void){LED11&#xff1b;while(1)&#xff1b;{LED10} }2.单个LED数码管以固定频率闪烁#include<reg51.h> sbit LED1P1^0;void Delay(unsigned in…

macos系统自动安装mysql_macos系统安装mysql

MacOS系统安装mysql一、下载官网下载链接地址&#xff1a;https://dev.mysql.com/downloads/mysql/二、安装打开文件是pkg包&#xff0c;双击进行安装&#xff1a;按照提示&#xff1a;点击最下面的MySQL控制按钮&#xff0c;启动数据库运行&#xff1a;在此可以启动和停止MySQ…

水晶报表中对某一栏位值进行处理_合并报表——非同一控制下的企业合并amp;同一控制下的企业合并...

【写在前面】长期股权投资企业的一种投资行为&#xff0c;投资方通过该行为享有被投资单位的股利分配、净利润等投资收益&#xff0c;处理的是母公司&#xff08;投资方&#xff09;的个别财务报表。只有控股合并才需要编制合并报表&#xff0c;意味着后续计量采用的是成本法。…

python测试框架untest怎么循环执行_unittest如何在循环遍历一条用例时生成多个测试结果...

引用自:http://blog.csdn.net/kaku21/article/details/42124593参考网址&#xff1a;http://programmaticallyspeaking.com/test-data-provider-using-python-metaclass.html使用TestNG进行测试的时候&#xff0c;允许使用外部数据源来驱动测试方法的执行&#xff0c;举个例子&…

python杨辉三角_yiduobo的每日leetcode 118.杨辉三角 amp;amp; 119.杨辉三角II

祖传的手艺不想丢了&#xff0c;所以按顺序写一个leetcode的题解。计划每日两题&#xff0c;争取不卡题吧。118.杨辉三角https://leetcode-cn.com/problems/pascals-triangle/119.杨辉三角IIhttps://leetcode-cn.com/problems/pascals-triangle-ii/经典的数学题。118题需要求出…

为什么链接不上mysql数据库_java链接不上数据库,怎么解决!

居正w去年刚好做过这个&#xff0c;给你贴下我的链接代码try { Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); //加载sqlserver JDBC驱动程序 //Class.forName("org.gjt.mm.mysql.Driver"); System.out.pr…

世界上最难的视觉图_世界上最长的蛇有多长?四川惊现55米洪荒巨蟒(图)

蛇&#xff0c;一直是站在食物链顶端的致命生物&#xff0c;蟒蛇更是能够吞食人类的恐怖巨兽。网传世界上最长的蛇有500米之长&#xff0c;名为“红海巨蛇”&#xff0c;已被证实为虚假传言&#xff0c;以地球现在的环境是不可能出现如此之大的蛇的。据说四川发现了罕见的55米长…

解析器 java_java 常用的解析工具

这里介绍两种 java 解析工具。第一种&#xff1a;java 解析 html 工具 jsoup第二种&#xff1a; java 解析 XML 工具 Dom4jjsoupjsoup是一个用于处理真实HTML的Java库。它提供了一个非常方便的API&#xff0c;用于提取和操作数据&#xff0c;使用最好的DOM&#xff0c;CSS和类似…

php 比java 快_php比java要快在哪里

php比java要快在哪里一些Java可以做的事情php做不了或者说要借助另外的工具才可以做&#xff0c;要但就开发网站这个事情来说&#xff0c;php确实是要比Java效率高&#xff0c;尤其是相对简单的项目。首先&#xff0c;Java的架构要比Php复杂&#xff0c;先不说各种开发框架&…

斯皮尔曼相关系数_惊艳!JASP相关系数矩阵及热力图

今天起我们新增一个案例数据&#xff0c;犯罪数据。这是mei国50个州关于犯罪率的一组数据&#xff0c;包括人口、面积、收入、文盲率、高中毕业率、霜冻天数、犯罪率共7个指标&#xff0c;现在我们想考察一下州犯罪率和其他因素间的关系。数据视图如下&#xff1a;数据取自《R语…

邻接矩阵和邻接表_[力扣743] 带权邻接表的单源最短路

题目链接743. 网络延迟时间 题目描述有 N 个网络节点&#xff0c;标记为 1 到 N。给定一个列表 times&#xff0c;表示信号经过有向边的传递时间。 times[i] (u, v, w)&#xff0c;其中 u 是源节点&#xff0c;v 是目标节点&#xff0c; w 是一个信号从源节点传递到目标节点的…

opencv java ubuntu_Ubuntu 16.04配置OpenCV 3.1.0 for Java

我们都知道&#xff0c;OpenCV是基于C的开源计算机视觉库&#xff0c;但是从2.4.4版本开始提供了Java绑定&#xff0c;也就是说&#xff0c;我们也可以使用Java来开发基于OpenCV的计算机视觉应用。目前&#xff0c;最新的版本是3.1.0&#xff0c;在本文中将会介绍如何中Ubuntu …

service 层 拼接的html 代码如何直接返回_字符串拼接,会走StringBuilder 吗?

前言最近在突然想到了String字符串拼接问题&#xff0c;于是做了一个demo测试了一下&#xff0c;到底String类型的字符串在拼接的时候&#xff0c;哪种情况下会走会走StringBulider进行字符串拼接&#xff0c;而哪种情况编译器会对代码进行优化&#xff1f;话不多说&#xff0c…

迁移学习训练集准确率一直上不去_可以提高你的图像识别模型准确率的7个技巧...

假定&#xff0c;你已经收集了一个数据集&#xff0c;建立了一个神经网络&#xff0c;并训练了您的模型。但是&#xff0c;尽管你投入了数小时(有时是数天)的工作来创建这个模型&#xff0c;它还是能得到50-70%的准确率。这肯定不是你所期望的。下面是一些提高模型性能指标的策…

华为云客户端_华为公布云手机计费清单,要不要光刻机也给出了答案

华为云手机一出来&#xff0c;大家的好奇心就上来了。这就是解决华为无芯片的代替方案。纷纷说道&#xff0c;可以绕开光刻机&#xff0c;光刻机瞬间变废铁。当时我还发布过文章分析&#xff1a;现在的云手机只是一个云端应用&#xff0c;并不是真正的云手机。需要在手机或电脑…