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 100
2 P2 101 200
3 P3 201 300
4 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 100
OK
127.0.0.1:6379> hmset {ip}:2 city P2 min 101 max 200
OK
127.0.0.1:6379> hmset {ip}:3 city P3 min 201 max 300
OK
127.0.0.1:6379> hmset {ip}:4 city P4 min 301 max 400
OK
//建立sorted set(member-score)
127.0.0.1:6379> zadd {ip}:end.asc 100 1 200 2 300 3 400 4
(integer) 4
127.0.0.1:6379> zrange {ip}:end.asc 0 -1
1) "1"
2) "2"
3) "3"
4) "4"
//查詢對應的區間(score)
127.0.0.1:6379> zrangebyscore {ip}:end.asc 90 +inf LIMIT 0 1
1) "1"
127.0.0.1:6379> zrangebyscore {ip}:end.asc 123 +inf LIMIT 0 1
1) "2"
127.0.0.1:6379> zrangebyscore {ip}:end.asc 100 +inf LIMIT 0 1
1) "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 city
1) "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"