馬蜂窩技術原創內容,更多幹貨請關注公衆號:mfwtech算法
隨着智能手機存儲容量的增大,以及相冊備份技術的普及,咱們能夠隨時隨地用手機影像記錄生活,在手機中存儲幾千張甚至上萬張照片已是很常見的事情。但另外一方面,當咱們想從這麼多張照片中去找到一張,也是一件麻煩事。數據庫
馬蜂窩做爲旅行玩樂平臺,但願實現「會玩的人」與「好玩的事」之間的鏈接。衆多旅行愛好者在這裏記錄和分享他們的旅行記憶,使馬蜂窩在旅遊 UGC 領域累積了大量內容。所以,不斷優化用戶在發佈內容時的體驗是咱們一直努力的主向。緩存
用照片、視頻記錄旅行是最直接的方式。本文將介紹馬蜂窩如何經過 App 地理相冊空間索引的應用,爲用戶提供直觀、好用的圖片分享服務。性能優化
要想讓用戶快速地找到想要分享的照片/視頻,咱們須要一個有效且合理的篩選手段,對用戶的相冊進行聚合、排序,提高用戶依託相冊去分享和記錄生活時易用性和便捷性。數據結構
首先要肯定聚合排序的篩選維度。照片的地理位置就是最直觀的分類維度;同時,記錄最近發生的事情符合用戶的發佈行爲習慣。所以咱們方案要知足的需求是:函數
本文說起的地理相冊服務在馬蜂窩 App 內主要有兩個落地場景。性能
「筆記」是以圖片、視頻爲主要呈現形式的旅行短內容分享。用戶發佈筆記的第一個環節就是從相冊中選擇須要發佈的照片/視頻,在新版 App 中,基於地理相冊服務結合馬蜂窩自有目的地數據,對用戶相冊進行按照地點維度的聚合分類,而且按照片/視頻的建立時間由近及遠的排序,提高用戶選擇發佈效率。優化
「足跡」這一產品的功能,旨在幫助馬蜂窩用戶以自動同步或手動點選去過的國家和地區這種更簡易的方式記錄旅行。在「個人足跡」中有一個場景,會鼓勵用戶對去過的但尚未發佈筆記的地點發布筆記。此時地理相冊服務能夠幫助用戶發佈相冊中以指定地點爲圓心,給定半徑範圍內的全部照片。編碼
初期咱們想到的方案比較直觀,也比較粗暴,就是對相冊進行遍歷後由服務端計算結果。具體來講,首先取出用戶全部攜帶地理信息的照片/視頻,而後將地理信息(經緯度)上傳服務端,由服務端進行聚合和篩選,返回給客戶端結果,可是這個方案有不少缺點。設計
文章開始咱們已經描述了目前用戶手機設備中的照片數量是成千上萬的,若是遍歷全部圖片,這上傳的數據體量是巨大的;同時,通常用戶照片的地理位置會有不少呈現出成簇彙集的狀態,由於通常咱們會在一個地點範圍內拍攝許多照片,這就致使了大量的重複聚類的計算。
若是要優化這個方案,針對第一個需求咱們能夠採用緩存+增量請求的方式,由於用戶分類數據是穩定的。可是針對給定範圍查詢的需求,咱們沒法作緩存,這就須要每次都請求服務端作大量的計算,對於時間的消耗是不能容忍的。
能夠看到,上述方案的挑戰主要在於用戶相冊中地理信息的數據量和重複度、依賴服務端計算搜索結果致使的性能問題和用戶體驗。通過調研咱們發現,基於地理空間點(經緯度)索引算法能夠很好地解決這些問題。
結合咱們的實際需求來理解地理空間點索引算法,即找到合適的方法來對地理空間中海量的座標點添加索引,從而對空間點進行快速查詢和排序的一種算法。
咱們對一些比較通用的地理空間點索引算法進行了選型比較,下面主要介紹 GeoHash 算法和 Google-S2 算法。
GeoHash 算法即地理位置距離排序算法。Geohash 是一種地理編碼,由 Gustavo Niemeyer 發明。它利用一種分級的數據結構,把空間劃分爲網格。
GeoHash 屬於空間填充曲線中的 Z 階曲線的實際應用。GeoHash 有一個和 Z 階曲線相關的性質,那就是一個點附近的地方 Hash 字符串老是有公共前綴,而且公共前綴的長度越長,這兩個點距離越近。因爲這個特性,GeoHash 就經常被用來做爲惟一標識符,好比在數據庫裏面可用 GeoHash 來惟一表示一個點。
GeoHash 這個公共前綴的特性就能夠用來快速的進行鄰近點的搜索。越接近的點一般和目標點的 GeoHash 字符串公共前綴越長。可是 Z 階曲線有一個比較嚴重的問題,就是它的突變性。在每一個 Z 字母的拐角,都有可能出現順序的突變,致使搜索臨近點的精確度較差,不能知足咱們的業務場景對精確度的要求。
S2 實際上是來自幾何數學中的一個數學符號 S²,它表示的是單位球。
S2 算法採用正方體投影的方式將地球展開,而後利用希爾伯特分形曲線將展開後的二維地球進行填充,完成了對三位地球的降維和分形,從而獲得空間座標點與希爾伯特分形曲線的函數關係,即將球面經緯度座標轉換成球面 xyz 座標,再轉換成正方體投影面上的座標,最後變換成修正後的座標在座標系變換,映射到 [0,2^30^-1] 區間,最後一步就是把座標系上的點都映射到希爾伯特曲線上。最終,映射到希爾伯特曲線上的點成爲 Cell ID,便是空間座標點的索引。
S2 的最大的優點在於精度高。Geohash 有 12 級,從 5000km 到 3.7cm,中間每一級的變化比較大。有時候可能選擇上一級會大不少,選擇下一級又會小一些。而 S2 有 30 級,從 0.7cm² 到 85,000,000km²,中間每一級的變化都比較平緩,接近於 4 次方的曲線。因此選擇精度時不會出現 Geohash 選擇困難的問題。
綜上,S2 算法可以知足咱們對於功能和精度上的要求,所以最終選擇 S2 算法做爲空間點索引算法的實現方案。
本文中的 App 地理相冊服務主要基於相冊索引數據操做、用戶相冊掃描、相冊索引服務和相冊地點分類計算四大模塊實現:
如下分別介紹。
相冊位置信息的索引採用數據庫做爲存儲介質,將用戶照片信息以及經過 S2 算法計算出來的 Cell ID 存儲到數據庫當中。其中,考量存儲的數量和對搜索和聚合經度的要求,存儲了從 Level4~Level16 經度級別的 Cell ID。
相冊索引數據操做模塊,由數據庫(DB)和數據庫操做層(DAO)組成。數據表的設計見下圖:
數據庫操做層(DAO)封裝了數據插入、刪除、查詢等基本操做的 API。
用戶相冊掃描模塊基於 iOS 原生提供的相冊查詢的 API,將用戶相冊的數據與本地數據庫中存儲的照片數據進行對比,提取出新增照片數據和用戶已經刪除的照片。
相冊索引服務模塊,是基於 S2 算法的相冊服務的核心模塊。模塊功能以下:
相冊地點分類計算模塊是計算用戶相冊的地點分類結果的核心模塊。該模塊的主體功能以下:
相冊索引服務模塊會在 App 啓動時更新服務,將本地數據與相冊數據同步。當用戶觸發地點相冊功能時,相冊地點分類計算模塊會先取出緩存在本地相冊地點分類計算結果展示給用戶,同時驅動相冊索引服務更新。
在收到更新服務更新完畢的通知後,首先向相冊請求 12Level 的全量去重的 Cell ID,而後將 Cell ID 上傳服務端由服務端計算分類,最後結合相冊索引服務的全量照片數據,計算照片的地點分類結果,緩存結果並渲染展示給用戶。
相冊索引服務模塊須要同步服務和用戶相冊的照片資源數據,找到新增數據,加入到服務數據庫中。最初設計的獲取新增數據方案以下:
Step.1 獲取全量的用戶相冊的數據
Step.2 遍歷用戶照片,查詢是否存在本地服務數據庫中
可是這個方案應用到照片量較大的手機上時,獲取新增照片的時延很高。排查後咱們發現緣由在於全量遍歷用戶相冊時延很高,同時在遍歷中頻繁查詢數據庫也比較耗時。通過調研發現,iOS 的用戶相冊有「最近項目」的相冊分類,該相冊分類下的資源只按照添加順序的倒序排列,即越新的照片越靠前。故將方案優化以下:
Step.1:從列表頭部截取 100 條
Step.2:將該 100 條追加爲新增照片
Step.3:判斷該 100 條中的最後一條,即新增時間最晚的一條,查詢是否存在於服務數據庫中
相冊地點分類計算模塊在得到服務端返回的分類結果(mdd_id 與 Cell ID 列表的映射關係)後,根據結果對本地服務數據庫中的照片進行分類。最初的方案以下:
Step.1:遍歷結果列表,得到每一個 mdd_id 映射的 Cell ID 列表
Step.2:將全部目的地維度照片分類結果,按照每一個結果集中照片最晚建立時間,即第一個照片的建立時間,進行倒序排序,得到按照地點維度和建立時間維度排序的地點相冊的最終計算結果。
這樣的方案致使在地點相冊首次計算的時候,用戶須要等待全部目的地下的結果計算完畢後才能展示給用戶,同時須要屢次按照建立時間排序,致使時延很高,冷啓動下用戶體驗不好。
爲此,咱們作出了方案優化,減小排序次數,同時經過漸進加載的方式優化用戶體驗。主要思路是相冊索引服務模塊的數據庫中,存儲照片的建立時間能夠經過 SQL 查詢,按照建立時間倒序排列的全部照片資源,獲取倒序排列的照片資源集合:
Step.1:每次從照片資源集合頭部取 1000 條照片
Step.2:將該 1000 張照片的分類結果渲染展示給用戶
Step.3:計算完全部照片的分類,通知結束渲染,計算完畢。
以上方案,將全量的本地照片資源以 1000 張爲一批次,進行漸進計算,同時漸進渲染,縮短了用戶的等待時間;同時,依託關係型數據庫的排序能力,減小排序次數,優化了性能。
目前,本文介紹的基於 Google-S2 算法實現的地點相冊在馬蜂窩 APP iOS 客戶端已經上線一段時間,而且爲筆記發佈量帶來了正向增加。可是這套方案在數據庫數據處理中已經對於 Google-S2 算法的使用上仍然有很大的優化和探索空間,後續咱們團隊也會對其不斷優化和深挖。
Google-S2 算法服務在馬蜂窩 App iOS 客戶端中的實現和落地,成果不只僅是知足了筆記發佈場景的探索,更使得客戶端具有了對於用戶相冊照片百米級精確度的索引和搜索的能力,能夠爲後續更多、更復雜的業務場景服務,相信在不遠的將來能爲用戶提供更便捷、更有趣的旅行記錄產品。
本文做者:王巖、王明友,馬蜂窩內容業務研發工程師。