GolangNote

Golang笔记

用LevelDB 作为地理数据库

Permalink

用Goleveldb 来作地理数据库,用到 Goleveldb、Geohash。利用leveldb 的byte ordered 特性可以做高效查询。

用LevelDB 作为地理数据库

Geohash 可以把一个地点的经度和纬度(46.770 -71.304)转为一个8位字符串,如:f2m616nn,还能缩成4个字符:f2m61 (46.8 -71.3),只是精度不同。

这样就可以用 4 digits geohash + a uniq id 的方式存放地点的位置信息。

生成一个 int64 uniq id:

Go: int64 uniq id
1
2
3
4
5
6
7
8
9
10
11
// NewKey generates a new key using time prefixed by 'K'
func NewKey() Key {
    return NewKeyWithInt(time.Now().UnixNano())
}

// NewKeyWithInt returns a key prefixed by 'K' with value i
func NewKeyWithInt(id int64) Key {
    key := bytes.NewBufferString("K")
    binary.Write(key, binary.BigEndian, id)
    return key.Bytes()
}

假设要生成的key 是下面这个样子:

plaintext: key
1
G201509262105dhv766K��Ϸ�Y�

完善上面的代码:

Go: GeoKey generates
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// NewGeoKey generates a new key using a position & a key
func NewGeoKey(latitude, longitude float64) GeoKey {
    t := time.Now().UTC()
    kint := t.UnixNano()
    kid := NewKeyWithInt(kint)
    // G + string date + geohash 6 + timestamped key 
    // G201508282105dhv766K....
    gk := geohash.EncodeWithPrecision(latitude, longitude, 6)
    ts := t.Format("2006010215")

    // modulo 10 to store 10mn interval
    m := t.Minute() - t.Minute()%10
    zkey := []byte("G" + ts + fmt.Sprintf("%02d", m) + gk)
    zkey = append(zkey, kid...)
    return zkey
}

生成GeoKey 列表:

Go: GeoKeyPrefix
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// GeoKeyPrefix return prefixes to lookup using a GeoKey and timerange
func GeoKeyPrefix(start, stop time.Time) []string {
    var res []string
    d := 10 * time.Minute
    var t time.Time
    t = start
    for {
        if t.After(stop) {
            break
        }

        key := "G" + t.Format("2006010215") + fmt.Sprintf("%02d", t.Minute()-t.Minute()%10)
        res = append(res, key)
        t = t.Add(d)
    }
    return res
}

查询数据库:

Go: 查询数据库
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
d := time.Duration(-10) * time.Minute
geoPrefixs := GeoKeyPrefix(time.Now().UTC().Add(d), time.Now().UTC())

// find adjacent hashes in m
// 1, 5003530
// 2, 625441
// 3, 123264
// 4, 19545
// 5, 3803
// 6, 610
gk := geohash.EncodeWithPrecision(lat, long, 4)
adjs := geohash.CalculateAllAdjacent(gk)
adjs = append(adjs, gk)

// for each adjacent blocks
for _, gkl := range adjs {

    // for each time range modulo 10
    for _, geoPrefix := range geoPrefixs {
        startGeoKey := []byte(geoPrefix + gkl)
        iter := s.NewIterator(util.BytesPrefix(startGeoKey), nil)

        for iter.Next() {
            log.Println(iter.Value())
        }
        iter.Release()
    }
}

用到的链接

原文《A blazing fast geo database with LevelDB, Go and Geohashes

Related articles

bolt 数据库的事务

Bolt 类似于 LMDB,这个被认为是在现代 kye/value 存储中最好的。但是又不同于 LevelDB,BoltDB 支持完全可序列化的ACID事务...

Write a Comment to "用LevelDB 作为地理数据库"

Submit Comment Login
Based on Golang + fastHTTP + sdb | go1.16.5 Processed in 1ms