about 1 year ago

上一篇文章中,我們已經說明完基本的架構以及索引和分片的選擇,接下來我們就要實際的來使用資料來進行一些分析,能用搜尋時就用搜尋,不能用搜尋時就改用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})

結果如下,看來是不行。

那要著麼辦呢?這時我們有三個辦法。

  1. 再增加一個code索引。
  2. 選擇 { "date" : 1 }{ "code" : 1}當索引。
  3. 片鍵修改為{ "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筆,並且我們知道,它是先找到8011shard0002,然後再去它裡面進行塞選SHARDING_FILTER

db.stocks.find({"$and" : [
    {"code" : "8111"},
    {"date" : {"$gte":ISODate("2017-01-01"),
                "$lt":ISODate("2017-01-29")}}
]}).explain ("executionStats")

結果如下。

二哈想找到20170101到20170129的所有資料。

指令如下。

db.stocks.find({"$and" : [
    {"date" : {"$gte":ISODate("2017-01-01"),
                "$lt":ISODate("2017-01-29")}}
]}).explain ("executionStats")

結果如下,因為結果輸出太長了,所以我只貼下一主要的輸出,不過這邊我來解釋一下分片是著麼來處理這塊,mongos會向所有分片通知說,我要20170101 ~ 20170102的資料,然後更分片就會開始作業,進行SHARDING_FILTER把自已分片的符合範圍的找出來,然後再送往mongos,下圖總共尋找到9194筆,但它事實上還有輸出每個分片所輸出的結果,再圖片下面的表格我有寫出來。

下表為各分片處理的結果與時間,其中做計時間executionTimeMillisEstimatemongodb裡預估的時間和實執行的時間不一定會相反,下表有個地方很奇怪,那就是shard0001為什麼花的時間會比其它時間多很多。

結果 估計時間
Shard0000 3063 30ms
Shard0001 3068 840ms
Shard0002 3030 20ms

我們先執行下面指令,來看看個分片的狀況。

db.stocks.getShardDistribution()

結果如下,但沒有發現什麼異常分片,每個分片容量都很均衡,好,這題目前也無解……。

二哈想找出當日交易最熱絡的股票

嗯嗯~這個需求的話我們就需要用到聚合工具來解決,首先老樣子我們先拆分流程,先假設他說的當日為20170101好囉,然後所謂的熱絡我們就先暫定用成交量最大的來決定,和之前一樣一個流程一個流程寫。

  1. 先塞選出所有20170101
  2. 再根據成交量進行排序。
  3. 然後取第一個。

然後根據以上步驟我們就產生了下面的寫法,然後就有答案囉。

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 
}

二哈想找出某日價格波動最高的股票

首先我們要先定義波動最高的股票,通常我們是都用標準差來代表波動度,但這邊有點兒麻煩,所以我直接取最高價與最低價的差最大的,然後我們和上面一樣來列流程。

  1. 先塞選出所有20170101
  2. 然後在計算出最高價與最低價的差,並存放在v這欄位。
  3. 然後根據v進行排序。
  4. 然後取第一個。

根據上面的步驟產生的聚合如下。

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}
])

結果如下,啊喲著麼怪怪,v225……不過也是有可能,問題可能出在模擬時標準差那個設定,不過算囉~這只是模擬先降。

{ "_id" : ObjectId("58612d243fa369100661abb9"), 
    "code" : "1566", "open" : 987, "v" : 225 }

~ 結語 ~

本篇文章簡單的說明完,你要如何使用這價股價資料,和進行一些簡單的分析,事實上它還可以進行更多的事情,例如程式交易或演算法交易之類,不過先說好這邊也只是將交易時點產生出來,你如果要實戰的話還需要去串接卷商的Api說起來也不算太難,對了下一篇文章我們就簡單的說一下程式交易相關的需求來試試 ~

← 30-26之MongoDB運用研究---股價應用模擬(1) 30-28之MongoDB運用研究---股價應用模擬(3) →
 
comments powered by Disqus