MongoDB建立表預設有一個_id欄位來作為autoincrement自增實現,而這個_id欄位類型是objectid類型(objectid 是12位元組的BSON類型)。ObjectId的詳細解釋。
而現在因為資料庫遷移,將項目中原mysql的一些locations表移植到mongodb上面。對於locations 使用mongodb 2d loc 索引能夠更快的進行資料檢索,同時避免原mysql需要經過複雜的經緯度查詢。
那麼資料移轉後主要的要求有
1、資料庫表的遷移不會影響表與表之間的關係,繼續保持表之間的主外鍵關係。
2、lat 與 lng 欄位在mongodb 新表中合并為一個欄位loc,同時對loc加以2d索引。
對於第二點主要影響是在代碼之中,可以看看這裡。就不做詳細描述了。
那麼現在主要要做的事情就是,維持表與表的關係與主外鍵關係。我們以event 與 對應的 event_locations為例
event中有一個外鍵location_id,location_id就是event_locations的主鍵id;而現在mongodb中建立event_locations表,同時要保持event_locations與event的主外鍵參考關聯性。
因為mongodb表中預設有著_id這樣一個主鍵,那現在我們的資料移轉以及mongodb event_locations表結構構建有兩種選擇
1、event_locations 主鍵以_id,即將原mysql event_locations 的id 替換為 _id。也就是如下表結構
查看原始碼列印協助
1 { "_id" : ObjectId("51243bdb383ca83413000000"), "topic_id" : 103874, "name" : "xixi aaahah", "address" : "xxxbbbb cccccc", "loc" : [ 30.11, 112.02 ], "created" : 1361329114, "updated" : 0 }
優缺點分析
優點就是 使用預設的_id主索引值,速度快檢索方便。_id自增不必維護,有mongodb自行完成。
缺點很明顯 event中的location_id外鍵,原來類型是int,現在改用_id objectid類型作為主鍵id,location_id類型必須要更改為char(32)了。且程式碼還需要一定的更改。
最大的缺點就是目前項目已經上線,直接更改資料庫表可能會帶來不可預知的問題;對於運營維護可能帶來很大壓力。且對於目前event已有的location_id資料需要進行同步更新。
2、保持原來的event_locations表結構,使用id繼續維持與event location_id的主外鍵關係。
| 代碼如下 |
複製代碼 |
{ "_id" : ObjectId("51243bdb383ca83413000000"), "id" : 2354, "topic_id" : 103874, "name" : "xixi aaahah", "address" : "xxxbbbb cccccc", "loc" : [ 30.11, 112.02 ], "created" : 1361329114, "updated" : 0 }
|
優缺點分析
優點 不必去更改event location_id的資料類型,且location_id的資料不必修改;只用將event_locations資料完整匯入到新表中即可。
缺點 要維持id的自增屬性,而mongodb預設是_id才有自增屬性的,所以我們要用某種方法實現id的自增實現。_id會佔用儲存空間(預設的_id原本可以刪除的,但考慮到以後可能會用到就儲存了)。
我現在採用的是第2種方式,主要原因如下
1、項目已上線,且已經有了使用者資料;對於直接更改資料表與操作資料會帶來很大的風險。強烈不推薦。
2、event_locations資料幾乎不用同步,只需要單嚮導入到mongodb 新表就好。
3、程式碼變動最小,目前程式碼更改就是新的loc(lng+lat)儲存與id的自增實現。而第一種除了這些,還需要把所有的id替換為_id,資料類型也要從int變為string。
在確定瞭解決方法之後那麼現在主要的難題就是怎麼實現id的自增?
目前也有兩種方法
1、傳統實現,每次在event_locations insert之前,全表id desc排序得到最大的id值,對id值+1既得到了最新的id值從而實現了自增。
2、使用mongodb的findAndModify()方法實現,獨立一個ids表用來記錄所有可能需要id自增的值。
同樣簡要說明下這兩種實現id自增的方法優劣。
第一種方法
優點 不用構建ids表(工具表),免於維護
缺點 每次都要進行全表索引排序且尋找到最大id那條記錄,在進行+1
第二種
優點 findandmodify() 原子操作(瞭解原子操作請查看這裡),尋找並修改,直接返回當前操作的這個文檔內容。
缺點 要構建維護ids表,即項目中所有表可能需要自增的表都有一條記錄存在於其中。
無論採用哪種方法都要修改程式碼,即id autoincrement部分需要程式實現。我採用的是第二種方法。
主要考慮還是效能問題,避免每次insert之前都要對event_locations進行全部的索引排序。而維護ids這樣的表會很簡單且效能上忽略不計,詳細表結構如下
| 代碼如下 |
複製代碼 |
{ "_id" : ObjectId("5147ca1a08c2c1bc12abdb4c"), "id" : 2356, "tablename" : "topic" }
// _id 預設主鍵,mongodb自增 // id 表中自增主索引值 // tablename 表名 |
擷取下一個id的核心代碼,繼承了mongodb的ids Model。其中一個方法專門來做這個事情。
| 代碼如下 |
複製代碼 |
/** * 構建ids用於儲存所有表中需要自增的id值,結合findandmodify()來返回對應表最新的id * @param string $tablename 表名 * @return int */ public function get_next_id($tablename = 'topic'){
// db.ids.findAndModify({update:{$inc: {id:1}},query:{tablename:'topic'},new:true}); $data = array( 'findAndModify' => 'ids', 'update' => array( '$inc' => array( 'id' => 1)), 'query' => array('tablename' => $tablename), 'new' => true ); $idrecord = $this->db()->command( $data ); $newid = $idrecord['value']; return $newid['id']; } // 等同於db.ids.findAndModify({query: {tablename: 'topic'}, update: {$inc: {id: 1}}}); |
這樣在擷取到最新的id值後,insert event_locations 時需要將id加入到sql語句中(原先id是autoincrement,id欄位沒有在sql中)。之後也就是同樣的商務程序了。
擴充:
有時候可能會遇到一些奇怪的需求,比如一張表裡面有兩個自增id要實現。。。如此,上面的表結構就不能實現了。
| 代碼如下 |
複製代碼 |
1 <pre>// _id 預設主鍵,mongodb自增 2 // fieldname 表中自增欄位名 3 // fieldval 表中自增id值 4 // tablename 表名</pre> |
如此就行了哦
有時候也會碰到一些奇怪的需求,比如要從指定的值如200開始id自增。
那麼其實用這樣一句就可以了
| 代碼如下 |
複製代碼 |
db.ids.findAndModify({query: {tablename: 'topic'}, update: {id: 200,tablename: 'topic'}});
|
代碼實現上就是注意要update時要把tablename也要傳遞進去了,因為update會更新所以的記錄,如果不寫上tablename那麼返回的文檔就只有一個id欄位了。。。有需要的自己擴充下吧需要注意的是event_locations id現在不是主鍵也沒有任何索引,除非你加上了;所以這裡還是強烈推薦加上索引
| 代碼如下 |
複製代碼 |
db.event_locations.ensureIndex({id: 1}); |