BLOG
Enjoy when you can, and endure when you must.
NOV 12, 2013/数据库
Redis学习:IP查询(一)

本文部分翻译自《Redis in Action》(Josiah L Carlson)。

利用IP定位用户以提供地方化的服务是目前Web的常用做法。使用Redis,我们可以很方便的实现该功能。

对于开发,我们可以从http://dev.maxmind.com/geoip/geolite下载免费的IP数据库。这个数据库包含两个重要的文件:Geo-LiteCity-Blocks.csv和GeoLiteCity-Location.csv,分别为IP段与城市ID的映射以及ID所对应的城市信息(如市、区/周/省、国家名称等)。

我们可以建立两张表,分别对应以上两个csv文件。

第一张表,我们可以将其放入ZSET中并以城市ID为member,以IP为score。当然,这里我们会对IP做一定的转换(即通过a * 256 * 256 * 256 + b * 256 * 256 + c * 256 + d转换为一个整数)以达到该目的,具体的代码实现如下:

def ip_to_score(ip_address):
    score = 0
    for v in ip_address.split('.'):
        score = score * 256 + int(v, 10)
    return score

接下来,我们就可以将数据库导入了,因为一个城市会对应多个IP,因此我们可以利用ZSET的特性,记录城市ID对应的第一个IP地址,通过IP范围来定位城市(具体之后来看)。以下是数据导入的代码:

def import_ips_to_redis(conn, filename):
    csv_file = csv.reader(open(filename, 'rb'))
    for count, row in enumerate(csv_file):
        start_ip = row[0] if row else ''
        if 'i' in start_ip.lower():
            continue
        if '.' in start_ip:
            start_ip = ip_to_score(start_ip)
        elif start_ip.isdigit():
            start_ip = int(start_ip, 10)
        else:
            continue
        city_id = row[2] + '_' + str(count)
        conn.zadd('ip2cityid:', city_id, start_ip)

另外一张表当然就是城市ID与详情的映射关系,可以用HASH来实现:

def import_cities_to_redis(conn, filename):
    for row in csv.reader(open(filename, 'rb')):
        if len(row) < 4 or not row[0].isdigit():
            continue
        row = [i.decode('latin-1') for i in row]
        city_id = row[0]
        country = row[1]
        region = row[2]
        city = row[3]
        conn.hset('cityid2city:', city_id,
            json.dumps([city, region, country]))

准备工作就绪,接下来可以开始查询工作了。

COMMENTS
LEAVE COMMNT