redis范围查询应用。
需求
根据IP找到对应的城市
原来的解决方案
oracle表(ip_country):
查询IP对应的城市:
1.把a.b.c.d这样格式的IP转为一个数字,例如为把210.21.224.34转为3524648994
2. select city from ip_country where ipstartdigital <= 3524648994 and 3524648994 <=ipenddigital
redis解决方案
我们先把上面的表简化一下:
id city min max
1 P1 0 1002 P2 101 2003 P3 201 3004 P4 301 400(注意:min/max组成的range之间不能有重叠)
主要思路就是用hmset存储表的每一行,并为每一行建立一个id(作为key)
然后把ip_end按顺序从小到大存储在sorted set当中,score对应该行的id
查询时,利用redis sorted set的范围查询特性,从sorted set中查询到id,再根据id去hmget
实验
//存储表的每一行
127.0.0.1:6379> hmset {ip}:1 city P1 min 0 max 100OK127.0.0.1:6379> hmset {ip}:2 city P2 min 101 max 200OK127.0.0.1:6379> hmset {ip}:3 city P3 min 201 max 300OK127.0.0.1:6379> hmset {ip}:4 city P4 min 301 max 400OK//建立sorted set(member-score)127.0.0.1:6379> zadd {ip}:end.asc 100 1 200 2 300 3 400 4(integer) 4127.0.0.1:6379> zrange {ip}:end.asc 0 -11) "1"2) "2"3) "3"4) "4"//查询对应的区间(score)127.0.0.1:6379> zrangebyscore {ip}:end.asc 90 +inf LIMIT 0 11) "1"127.0.0.1:6379> zrangebyscore {ip}:end.asc 123 +inf LIMIT 0 11) "2"127.0.0.1:6379> zrangebyscore {ip}:end.asc 100 +inf LIMIT 0 11) "1"//解释://zrangebyscore {ip}:end.asc 90 +inf LIMIT 0 1//表示查找大于等于90的第一个值。(+inf在Redis中表示正无穷大)//该语句返回值score=1,与hmset当中的id对应,因此可以通过hmget查找城市了://查找城市127.0.0.1:6379> hmget {ip}:1 city1) "P1"注意在设计redis key时,采用了统一的前缀:{ip}
这是为了使得这些IP相关的数据都落在同一台redis server中(我们的redis以集群形式部署且采取一致性哈希),往后数据迁移什么的会更方便
实操
从数据库中导出的得到的文本是这样的(选取几行为例子):
ipcountry_tab_orderby_end_asc.txt:
"IPSTART" "IPSTARTDIGITAL" "IPEND" "IPENDDIGITAL" "COUNTRY" "CITY" "TYPE" "REGISTRY" "ADRESS" "PROVINCE"
"1.184.0.0" 28835840 "1.184.127.255" 28868607 "中国" "广州市" "" "" "" "广东省""1.184.128.0" 28868608 "1.184.255.255" 28901375 "中国" "广州市" "" "" "" "广东省""1.185.0.0" 28901376 "1.185.95.255" 28925951 "中国" "南宁市" "" "" "" "广西省"1.生成批量的hmset命令及zadd命令
写个小程序来生成:
import java.io.File;
import java.io.IOException;import java.io.UnsupportedEncodingException;import java.util.ArrayList;import java.util.List;import org.apache.commons.io.FileUtils;import org.apache.commons.lang3.StringUtils;public class IpCountryRedisImport { public static void main(String[] args) throws IOException { File file = new File("E:/doc/ipcountry_tab_orderby_end_asc.txt"); File hmsetFile = new File("E:/doc/ip_country_redis_cmd.txt"); File zaddFile = new File("E:/doc/ip_country_redis_zadd.txt"); List lines = FileUtils.readLines(file); int i = 0; StringBuilder rows = new StringBuilder(); StringBuilder ends = new StringBuilder(); for (String str : lines) { if (StringUtils.isEmpty(str)) { continue; } //skip first line if (i == 0) { i++; continue; } i++; //"IPSTART" "IPSTARTDIGITAL" "IPEND" "IPENDDIGITAL" "COUNTRY" "CITY" "TYPE" "REGISTRY" "ADRESS" "PROVINCE"