LBS-查找附近的人-mongodb-spring實(shí)現(xiàn)

前面介紹了地理坐標(biāo)基礎(chǔ)知識(shí)莫鸭,mysql躲撰、redis门粪、mongodb基礎(chǔ)知識(shí)的解決方案

接下來我們開始介紹mongodb結(jié)合spring-data的解決方案赴背。

通過上一篇的學(xué)習(xí)铣焊,我們可以知道使用 GeoJSON對(duì)象 來保存用戶的位置數(shù)據(jù)比較合適逊朽。

在開始前我們先初始化一些測(cè)試數(shù)據(jù),為了后面的測(cè)試我們一開始就通過for循環(huán)創(chuàng)建600w個(gè)隨機(jī)數(shù)據(jù)。

創(chuàng)建數(shù)據(jù)

創(chuàng)建集合-插入數(shù)據(jù)-建立索引

#創(chuàng)建集合
db.createCollection("mongoGeoUser")
#插入數(shù)據(jù)
db.mongoGeoUser.insert({
   _id: 1,
   name: "user1",
   createdAt: new Date(),
   location: { type: "Point", coordinates: [ 120.1333737373, 30.2535809303 ] },
} );
#在location字段上創(chuàng)建索引
db.mongoGeoUser.createIndex({ location: "2dsphere" })

spring-data實(shí)現(xiàn)

@Document(collection = "mongoGeoUser")  //指定文檔名稱
public class MongoGeoUser {
    @Id
    private Long id;
    private String name;
    private GeoJsonPoint location;
    private Date createdAt;

    public MongoGeoUser(Long id, String name, GeoJsonPoint location, Date createdAt) {
        this.id = id;
        this.name = name;
        this.location = location;
        this.createdAt = createdAt;
    }
    //geter and seter
}
@RunWith(SpringRunner.class)
@SpringBootTest
public class MongoDBTests {
    @Autowired
    private MongoTemplate mongoTemplate;
    /**
     * 更新或插入對(duì)象
     */
    @Test
    public void createMongoGeoUsers() {//批量添加,600w條數(shù)據(jù)比redis添加慢了很多很多曲伊,要等一小會(huì)的叽讳,建議泡杯茶,回頭復(fù)習(xí)一下之前的文章
        for (int i = 0; i < 6000000; i++) {
            BigDecimal lng = new BigDecimal(Math.random() * (130 - 100) + 100).setScale(10, BigDecimal.ROUND_HALF_UP);
            BigDecimal lat = new BigDecimal(Math.random() * 20 + 20).setScale(10, BigDecimal.ROUND_HALF_UP);

            MongoGeoUser mongoGeoUser = new MongoGeoUser(new Long(i), "user1", new GeoJsonPoint(lng.doubleValue(), lat.doubleValue()), new Date());
            mongoTemplate.save(mongoGeoUser);
        }
    }
}

創(chuàng)建好600w條數(shù)據(jù)后坟募,開始我們的測(cè)試岛蚤,切記,不要忘記建立索引

> db.mongoGeoUser.count();
6000000

在球面中查詢附近1km~5km的點(diǎn)

@Test
public void nearSphere() {
    Query query = new Query(Criteria.where("location")
            .nearSphere(new GeoJsonPoint(120.666666666, 30.888888888))
            .maxDistance(5000).minDistance(1000));
    List<MongoGeoUser> venues = mongoTemplate.find(query, MongoGeoUser.class);

    for (MongoGeoUser venue : venues) {
        System.out.println(venue);
    }
}

MongoDB支持查詢數(shù)據(jù)庫中的地理位置婿屹,并同時(shí)計(jì)算與給定原點(diǎn)的距離灭美。通過地理近似查詢,可以表達(dá)如下查詢:“查找周圍1km~5km內(nèi)附近的人”昂利,并返回距離信息届腐。使用上面的 Query 是無法得到距離信息的

@Test
public void nearQuery() {
    long startTime = System.currentTimeMillis();
    NearQuery near = NearQuery
            .near(new GeoJsonPoint(120.666666666, 30.888888888))
            .spherical(true)
            .maxDistance(5, Metrics.KILOMETERS)
            .minDistance(1, Metrics.KILOMETERS)
            .num(10);
    GeoResults<MongoGeoUser> results = mongoTemplate.geoNear(near, MongoGeoUser.class);

    long endTime = System.currentTimeMillis();
    System.out.println("程序運(yùn)行時(shí)間:" + (endTime - startTime) + "ms");

    for (GeoResult<MongoGeoUser> result : results) {
        System.out.println(result);
    }
}

上面兩個(gè)方法就是我們解決查找附近的人的主要解決方案铁坎,網(wǎng)上其他的方案都住要使用 傳統(tǒng)坐標(biāo)對(duì) 的方式存儲(chǔ)位置信息,本文采用的是 GeoJSON對(duì)象 , 實(shí)際上兩個(gè)存儲(chǔ)方案沒有很大的差別犁苏,官方建議再球面上的坐標(biāo)使用 GeoJSON對(duì)象 來存儲(chǔ)硬萍,傳統(tǒng)坐標(biāo)對(duì) 存儲(chǔ)的數(shù)據(jù)再計(jì)算球面數(shù)據(jù)的時(shí)候也可以指定 spherical(true) 來轉(zhuǎn)化成球面的計(jì)算值,再距離不是很大的情況下具體采用什么存儲(chǔ)方案沒有很大的差別围详。

最后還是到了我們關(guān)心的運(yùn)行效率朴乖,上面兩個(gè)方案的效率大約都是 300ms 左右的時(shí)間。對(duì)比上一篇的redis方案助赞,稍稍慢了一點(diǎn)點(diǎn)买羞,可能一個(gè)是內(nèi)存數(shù)據(jù)庫,一個(gè)不是吧雹食,但是不管怎么樣都還是符合生產(chǎn)環(huán)境的使用的畜普。

上面的 NearQueryQuery 還有很多其他的參數(shù)和查詢方法,比如 矩形內(nèi)的點(diǎn)群叶,還有平面內(nèi)的坐標(biāo)計(jì)算等吃挑。需要分頁的應(yīng)用也是可以分頁操作的。

其他具體的參數(shù)可以參考 spring-data-mongo文檔mongodb官方文檔

設(shè)置自動(dòng)過期

MongoDB也可以像redis那樣為key設(shè)置一個(gè)過期時(shí)間街立,但是redis-geo是采用zset實(shí)現(xiàn)的舶衬,zset無法對(duì)member設(shè)置過期時(shí)間。MongoDB對(duì)過期時(shí)間字段設(shè)置索引的方式來處理自動(dòng)過期赎离。

上面的 createdAt 字段就是用來設(shè)置過期時(shí)間的逛犹。

創(chuàng)建索引

db.log_events.createIndex({ "createdAt": 1 }, { expireAfterSeconds: 3600 })

該記錄會(huì)在 createdAt 時(shí)間的 expireAfterSeconds 秒后失效。

也可以設(shè)置一個(gè)特殊的過期時(shí)間點(diǎn)

db.log_events.insert( {
   "expireAt": new Date('July 22, 2013 14:00:00'),
   "logEvent": 2,
   "logMessage": "Success!"
} )

設(shè)置索引

db.log_events.createIndex( { "expireAt": 1 }, { expireAfterSeconds: 0 } )

結(jié)束語

到此我們的LBS-查找附近的人的所有解決方案就已經(jīng)結(jié)束了蟹瘾,各位讀者可以根據(jù)自己項(xiàng)目的情況需求來選擇合適的方案圾浅,不管是mysql掠手、redis還是mongodb都是在300w甚至600w數(shù)據(jù)的下測(cè)試過憾朴,各個(gè)方案都有自己的優(yōu)點(diǎn)和缺點(diǎn)。

不同類型的APP對(duì)LBS系統(tǒng)的讀寫壓力完全不同喷鸽。例如众雷,對(duì)于附近商家、美甲等O2O類的APP做祝,其更新和獲取LBS數(shù)據(jù)的頻率很低砾省,但是對(duì)于打車類APP,因?yàn)樾枰l繁的更新地理位置數(shù)據(jù)混槐,LBS后臺(tái)需要承擔(dān)的讀寫壓力要比一般的APP壓力大编兄。在快的公開的資料中LBS系統(tǒng)每秒的讀寫次數(shù)比居然達(dá)到4:1。

本系列文章都是基于靜態(tài)的數(shù)據(jù)進(jìn)行測(cè)試声登,當(dāng)時(shí)沒有寫入的操作狠鸳,如果寫壓力過大也是會(huì)導(dǎo)致查詢的效率下降的揣苏。當(dāng)然如果寫入的壓力過大通過主從和讀寫分離也還是可以處理的,如果是數(shù)據(jù)量過大mongodb還有分片的架構(gòu)可以處理件舵,總之現(xiàn)在給出來的幾個(gè)方案都是被大量的實(shí)踐所驗(yàn)證過的卸察,大膽放心的使用就行。

系統(tǒng)不要過度架構(gòu)铅祸,如果可以遇見數(shù)據(jù)的增長(zhǎng)量的話坑质,選擇一些簡(jiǎn)單的方案可以在后面節(jié)約不少時(shí)間。

文章同步發(fā)布在博客临梗,LBS-查找附近的人-mongodb實(shí)現(xiàn)-基礎(chǔ)知識(shí)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末涡扼,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子盟庞,更是在濱河造成了極大的恐慌壳澳,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,816評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件茫经,死亡現(xiàn)場(chǎng)離奇詭異巷波,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)卸伞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門抹镊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人荤傲,你說我怎么就攤上這事垮耳。” “怎么了遂黍?”我有些...
    開封第一講書人閱讀 158,300評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵终佛,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我雾家,道長(zhǎng)铃彰,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,780評(píng)論 1 285
  • 正文 為了忘掉前任芯咧,我火速辦了婚禮牙捉,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘敬飒。我一直安慰自己邪铲,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,890評(píng)論 6 385
  • 文/花漫 我一把揭開白布无拗。 她就那樣靜靜地躺著带到,像睡著了一般。 火紅的嫁衣襯著肌膚如雪英染。 梳的紋絲不亂的頭發(fā)上揽惹,一...
    開封第一講書人閱讀 50,084評(píng)論 1 291
  • 那天晌纫,我揣著相機(jī)與錄音,去河邊找鬼永丝。 笑死锹漱,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的慕嚷。 我是一名探鬼主播哥牍,決...
    沈念sama閱讀 39,151評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼喝检!你這毒婦竟也來了嗅辣?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,912評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤挠说,失蹤者是張志新(化名)和其女友劉穎澡谭,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體损俭,經(jīng)...
    沈念sama閱讀 44,355評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蛙奖,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,666評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了杆兵。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片雁仲。...
    茶點(diǎn)故事閱讀 38,809評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖琐脏,靈堂內(nèi)的尸體忽然破棺而出攒砖,到底是詐尸還是另有隱情,我是刑警寧澤日裙,帶...
    沈念sama閱讀 34,504評(píng)論 4 334
  • 正文 年R本政府宣布吹艇,位于F島的核電站,受9級(jí)特大地震影響昂拂,放射性物質(zhì)發(fā)生泄漏受神。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,150評(píng)論 3 317
  • 文/蒙蒙 一政钟、第九天 我趴在偏房一處隱蔽的房頂上張望路克。 院中可真熱鬧,春花似錦养交、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至驮履,卻和暖如春鱼辙,著一層夾襖步出監(jiān)牢的瞬間廉嚼,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評(píng)論 1 267
  • 我被黑心中介騙來泰國(guó)打工倒戏, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留怠噪,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,628評(píng)論 2 362
  • 正文 我出身青樓杜跷,卻偏偏與公主長(zhǎng)得像傍念,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子葛闷,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,724評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容