将本文以行表形式存储于关系型数据库中的IP信息库,通过转换,存储到key-value型的Redis库中,以加快查询的速度。本文通过使用Redis的散列类型和有序集合类型来实现这种需求。
在工程中常有这样的需求,即给定IP(本文一律考虑将点分十进制的ip转为无符号整型),从(全球)IP库中查找相关信息。若将IP库存储于关系型数据库中(本文仅截取部分字段用于阐明),其形式大概如下:
TAGID | FROMIP | TOIP | INFO1 | INFO2 |
---|---|---|---|---|
TAG01 | 1779830784 | 1779831039 | 湖南省 | 电信 |
TAG02 | 987332096 | 987332351 | 江苏省镇江市 | 电信 |
TAG03 | 2026831104 | 2026831359 | 江西省南昌市 | 移动 |
TAG04 | 2059661056 | 2059661311 | 江苏省淮安市 | 联通 |
TAG05 | 832214272 | 832214527 | 江苏省南京市 | 广电网 |
假设要查询给定IP的INFO1和INFO2信息,SQL语句如下:
SELECT INFO1,INFO2 WHERE IP >= FROMIP AND IP <= TOIP;
这样的语句查询关系型是数据库是比较耗时的。下面设法将上述数据存储到Redis中,主要借助Redis的散列类型和有序集合类型。
建表过程:
1. 将表中的每一个行以TAGID作为key,其余值作为value,以散列类型作为value的数据结构进行存储;
2. 将FROMIP和TAGID的映射关系(以FROMIP作为score),以有序集合的形式进行存储,这样所有的FROMIP将从小到大的顺序存储在Redis中;
查询过程(以java客户端jedis为例):
1. 将ip转换为无符号整型
2. 从redis有序集合类型中查找全球库中小于该ip的最大fromip,即确定该IP所在的起始区间;
jedis.zrevrangeByScore(key, max, min, offset, count);
key:有序集合类型的key值,目前为"fromipscore"
max:待查询的无符号整型IP
min:置为-1
offset:置为0,返回结果集的起始偏移量
count:置为1,只返回符合条件的第一条
如果查找不到,则直接返回,即该IP在库中查找不到;否则进行步骤3操作
3. 将步骤2返回的结果,作为新的key,查找redis散列类型的数据结构,并进行比较以确定该IP是否在上一步确定的区间内
Map<String, String> val = jedis.hgetAll(key);
val中包含的示例如下:
{INFO2=联通, INFO1=江苏省南京市, TOIP=1910858495, FROMIP=1910858240}
从val中取出toip的值,转换为整型,并与待查找的ip进行比较;
如果待查询ip <= toip;则 val即为查询的结果集;否则查找不到
由于有序集合使用散列表和跳跃表实现,因此确定IP所在的起始区间时间复杂度不超过O(lgn);其次查找散列表的时间复杂度,在不发生冲突时是常数。下面在shell端,对上面过程进行演示,加深理解。
- 将每行数据以散列表形式存储
127.0.0.1:6379> hmset TAG01 FROMIP 1779830784 TOIP 1779831039 INFO1 湖南省 INFO2 电信
OK
127.0.0.1:6379> hmset TAG02 FROMIP 987332096 TOIP 987332351 INFO1 江苏省镇江市 INFO2 电信
OK
127.0.0.1:6379> hmset TAG03 FROMIP 2026831104 TOIP 2026831359 INFO1 江西省南昌市 INFO2 移动
OK
127.0.0.1:6379> hmset TAG04 FROMIP 2059661056 TOIP 2059661311 INFO1 江苏省淮安市 INFO2 联通
OK
127.0.0.1:6379> hmset TAG05 FROMIP 832214272 TOIP 832214527 INFO1 江苏省南京市 INFO2 广电网
OK
- 把FROMIP作为SCORE,将TAGID以有序集合进行存储,key为fromip:score
127.0.0.1:6379> zadd fromip:score 1779830784 TAG01
(integer) 1
127.0.0.1:6379> zadd fromip:score 987332096 TAG02
(integer) 1
127.0.0.1:6379> zadd fromip:score 2026831104 TAG03
(integer) 1
127.0.0.1:6379> zadd fromip:score 2059661056 TAG04
(integer) 1
127.0.0.1:6379> zadd fromip:score 832214272 TAG05
(integer) 1
- 查询
IP为1779830785,该IP的tagid为TAG01
127.0.0.1:6379> zrevrangebyscore fromip:score 1779830785 -1 limit 0 1
1) "TAG01"
127.0.0.1:6379> hgetall TAG01
1) "FROMIP"
2) "1779830784"
3) "TOIP"
4) "1779831039"
5) "INFO1"
6) "\xe6\xb9\x96\xe5\x8d\x97\xe7\x9c\x81"
7) "INFO2"
8) "\xe7\x94\xb5\xe4\xbf\xa1"
127.0.0.1:6379>
比较1779830785和结果集中的TOIP即可判断