上一篇文章中,我們已經說明完基本的架構以及索引和分片的選擇,接下來我們就要實際的來使用資料來進行一些分析,能用搜尋時就用搜尋,不能用搜尋時就改用aggreagate framework
,然後如果再不能的話則用mapreduce
。
這二貨根本沒有想需求,只是想說來分析一下,但分析啥也沒說,然後還要我們幫他想一下,然後還用這種表情看我,一臉用這種事情還用問我的表情。
然後我們只能乖乖的幫他想幾個。
那我們先開始吧。
呃對了,雖然上一篇文章中,我們已經將索引與片鍵選出來了,分別為
索引 : { "date" : 1 , "code" : 1 }
片鍵 : { "code" : 1 }
但咱們突然想到一件事,你要建立的片鍵,必須要有索引,當我們的索引是複合索引,這樣我們還可以使用{ "code" : 1 }
來建立嗎? 我們來試試。
db.stocks.ensureIndex({ "date" : 1 , "code" : 1 })
sh.enableSharding("test")
sh.shardCollection("test.stocks",{"code":1})
結果如下,看來是不行。
那要著麼辦呢?這時我們有三個辦法。
code
索引。{ "date" : 1 }
與{ "code" : 1}
當索引。{ "date" : 1 , "code" : 1 }
。要選那個呢,首先先來說說第一個,增加一個索引,缺點就在於需要更多的空間,而且這索引搜尋時幾乎不太用到,因為幾乎被原本的 { "date" : 1 , "code" : 1 }
可取代。
那如果是刪除掉原本的,而直接用改為{ "code" : 1 }
與{ "date" : 1}
第二個辦法呢? 啊喲~降好像真的比較好,空間會比第一個辦法的兩種索引還少,而且兩個索引也幾乎都可用到,先暫定你,第二種辦法。
那第三種呢?片鍵修改為{ "date" : 1 , "code" : 1 }
直接否定,因為這樣會變成升序片鍵,新增只會在某個chunk
然後一直分發一直分發,會吃很多壓力,反棄。
所以這邊咱們就決定選辦法二,選擇 { "date" : 1 }
與{ "code" : 1}
當索引
db.stocks.ensureIndex({"code" : 1 })
db.stocks.ensureIndex({"date" : 1 })
sh.enableSharding("test")
sh.shardCollection("test.stocks",{"code":1})
別忘了還要開啟平衡器。
use config
sh.setBalancerState(true)
但我們執行上面的的操作sh.shardCollection("test.stocks",{"code":1})
卻出錯了…… ,網路上有人說用3.2.6
就可以,但我的已經是3.4
了啊= =
{ "code" : 50, "ok" : 0, "errmsg" : "Operation timed out" }
這時我換個方法試試,先分片再建資料,嗯可以,不過不知道為啥,我建到一定數量時,會開始不太好建,沒有全部新增進去的感覺,只建了47577001
筆,不過這問題改天在慢慢找。
根據上面的需求一個一個慢慢的來說明。
這個非常的簡單,如下指令,不過我們重點是要看詳細內容
db.stocks.find({"code" : "8111"}).explain("executionStats")
下圖為結果,從下圖可知尋找到5000筆
股價資料,並且SINGLE_SHARD
也說明它是使用片鍵來尋找,並且是從分片shard0002
,尋找到,並花費了12ms
。
那我們來根據date
來排序看看。
db.stocks.find({"code" : "8111"}).sort({"date":1}).explain("executionStats")
花費時間增加了十五倍203ms
,並且memUsage
告訴了我們記憶體花費了560000
大約為560kb
這是到還好,不過這邊要注意,如果這種股價和日期一起用然後排序的搜尋越用越多的話,就要考慮空間換取速度了,也就是補建立索引。
這也是不難,如下,花費了1051ms
並且找回了28
筆,並且我們知道,它是先找到8011
在shard0002
,然後再去它裡面進行塞選SHARDING_FILTER
。
db.stocks.find({"$and" : [
{"code" : "8111"},
{"date" : {"$gte":ISODate("2017-01-01"),
"$lt":ISODate("2017-01-29")}}
]}).explain ("executionStats")
結果如下。
指令如下。
db.stocks.find({"$and" : [
{"date" : {"$gte":ISODate("2017-01-01"),
"$lt":ISODate("2017-01-29")}}
]}).explain ("executionStats")
結果如下,因為結果輸出太長了,所以我只貼下一主要的輸出,不過這邊我來解釋一下分片是著麼來處理這塊,mongos
會向所有分片通知說,我要20170101 ~ 20170102
的資料,然後更分片就會開始作業,進行SHARDING_FILTER
把自已分片的符合範圍的找出來,然後再送往mongos
,下圖總共尋找到9194
筆,但它事實上還有輸出每個分片所輸出的結果,再圖片下面的表格我有寫出來。
下表為各分片處理的結果與時間,其中做計時間executionTimeMillisEstimate
是mongodb
裡預估的時間和實執行的時間不一定會相反,下表有個地方很奇怪,那就是shard0001
為什麼花的時間會比其它時間多很多。
結果 | 估計時間 | |
---|---|---|
Shard0000 | 3063 | 30ms |
Shard0001 | 3068 | 840ms |
Shard0002 | 3030 | 20ms |
我們先執行下面指令,來看看個分片的狀況。
db.stocks.getShardDistribution()
結果如下,但沒有發現什麼異常分片,每個分片容量都很均衡,好,這題目前也無解……。
嗯嗯~這個需求的話我們就需要用到聚合工具來解決,首先老樣子我們先拆分流程,先假設他說的當日為20170101
好囉,然後所謂的熱絡我們就先暫定用成交量最大的來決定,和之前一樣一個流程一個流程寫。
20170101
。然後根據以上步驟我們就產生了下面的寫法,然後就有答案囉。
db.stocks.aggregate([
{"$match" :
{"date" :
{"$gte":ISODate("2017-01-01"),
"$lt":ISODate("2017-01-02")}}},
{ "$sort" : { "volume" : -1} },
{ "$limit" : 1}
])
結果如下。
{
"_id" : ObjectId("586128a18a97380b2cd52d56"),
"code" : "6705",
"date" : ISODate("2017-01-01T14:26:35.368Z"),
"open" : 150, "heigh" : 161, "close" : 150, "low" : 150,
"volume" : 100995
}
首先我們要先定義波動最高的股票,通常我們是都用標準差來代表波動度,但這邊有點兒麻煩,所以我直接取最高價與最低價的差最大的,然後我們和上面一樣來列流程。
20170101
。v
這欄位。v
進行排序。根據上面的步驟產生的聚合如下。
db.stocks.aggregate([
{"$match" :
{"date" :
{"$gte":ISODate("2017-01-01"),
"$lt":ISODate("2017-01-02")}}},
{ "$project" : { "code" : 1 , "open" : 1,
"v" : { $subtract : [ "$heigh","$low" ] } } },
{ "$sort" : { "v" : -1} },
{ "$limit" : 1}
])
結果如下,啊喲著麼怪怪,v
為225
……不過也是有可能,問題可能出在模擬時標準差那個設定,不過算囉~這只是模擬先降。
{ "_id" : ObjectId("58612d243fa369100661abb9"),
"code" : "1566", "open" : 987, "v" : 225 }
本篇文章簡單的說明完,你要如何使用這價股價資料,和進行一些簡單的分析,事實上它還可以進行更多的事情,例如程式交易或演算法交易之類,不過先說好這邊也只是將交易時點產生出來,你如果要實戰的話還需要去串接卷商的Api
說起來也不算太難,對了下一篇文章我們就簡單的說一下程式交易相關的需求來試試 ~