MongoDB 5.0 不久前已經發布了,有鑒於各繁體中文新聞稿各種神奇翻譯,還是自己讀英文版,順便記個筆記。
讓我們來看看有什麼更新項目吧!


版本號

  1. 一樣維持每年一次大版本號更新(1.0, 2.0, 3.0 這種)
  2. 小版本號約為一個季度一次(5.1, 5.2 5.3)
  3. BUG修正小版號則會隨時發布(5.1.1, 5.1.2)

API 版本控制

現在可以指定你的 Application 要使用哪種版本的 MongoDB API。

這個功能一部分源自於上面版本號的更新,現在 MongoDB 更新速度更快,也代表使用 MongoDB 的 Application 需要更頻繁更新,這會讓開發者或者穩定的營運面很棘手,要去修改穩定或者沒問題的程式碼是有很大的風險的。於是這次 5.0 版本官方定義了常用的功能和指令,讓使用者能夠自行決定使用其版本,而這些都不會隨著資料庫版本更新而受到影響,官方是號稱長達數年(years)。

閱讀全文 »

之前使用 OracleSequence 很習慣,對於產生唯一且遞增序號功能覺得又快又方便,查了一下 MongoDB 沒有這個功能,於是只好自己土炮一個。


方法

使用 FindOneAndModify

結束。

.
.
.

這樣肯定會被打。

設計概念

於 MongoDB 建立一筆資料(可以是任意 Collection),且該資料有一整數型態欄位。當我們需要 Sequence number 時,只需要去取用它,每次取用時都遞增 1,並回傳遞增後的結果即可。程式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private static async Task<long> GetAutoIncrementValue()
{
var globalCollection = MongoHelper.GetGlobalVariableCollection();

var filter = Builders<GlobalVariableEntity>.Filter
.Eq(x => x.FieldName, "SequenceNumber");
var update = Builders<GlobalVariableEntity>.Update
.Inc(x => x.FieldValue, 1);
var option = new FindOneAndUpdateOptions<GlobalVariableEntity>
{
IsUpsert = false,
ReturnDocument = ReturnDocument.After
};

var entity = await globalCollection
.WithReadPreference(ReadPreference.Primary)
.FindOneAndUpdateAsync(
filter, update, option);

return entity.FieldValue;
}
閱讀全文 »

由於對 Google sheet 內建函式不常用,但腦海總浮現之前查過的印象,還是記錄一下好了。


比較樣本數相同

比較兩欄的資料是否有差異,這邊可以直接用 =,如果需要判斷大小寫或確切數值,則可以用 EXACT

Way1: =A1=B1
Way2: =EXACT(A1,B1)

Column 1 Column 2 Equal Mark Exact()
AAA AAA TRUE TRUE
BBB BBB TRUE TRUE
CCC NotEQual FALSE FALSE
DDD ddd TRUE FALSE

比較樣本數量不同

=ArrayFormula(iferror(if(match(F2:F7,G2:G5,0),"FoundMatch","")))

Column 1 Column 2 Result
AAA AAA FoundMatch
BBB BBB FoundMatch
CCC NotEQual
DDD ddd FoundMatch
EEE
閱讀全文 »

由於對 Google sheet 內建函式不常用,但腦海總浮現之前查過的印象,還是記錄一下好了。


Group 測試數據:

GroupColumn CountColumn SumColumn
AAA 10 100
BBB 20 200
CCC 30 300
AAA 40 400

GroupColumn 進行 GroupBy 預期應該是:

GroupColumn CountColumn SumColumn
AAA 50 500
BBB 20 200
CCC 30 300

語法:

1
=QUERY(rawData!A1:C4,"select A, count(B), sum(C) group by A")
  • rawData 為 sheet 名稱
  • A1:C4 為資料範圍

如果想要替 Column 命名,需使用 label 語法,例如 label A 'AliasName'

閱讀全文 »

本文介紹改用 Lua 方式存取 Redis,以省下連線、存取次數。

Demo內容

  1. 先在 redis 建立一個 key
  2. 以 lua 方式執行,把值取出來,加上整數 123,寫回去 redis
  3. 以一般指令方式,把值取出來,加上整數 123,寫回去 redis

建立連線機制

1
2
3
4
5
private const string RedisHost = "127.0.0.1:6379";
private const string TestKey = "TestRedisKey";

using var conn = await ConnectionMultiplexer.ConnectAsync(RedisHost);
var redisDb = conn.GetDatabase();

一般存取方式

當有一些邏輯比較複雜就必須把值取回程式中運算。

1
2
3
4
5
6
7
8
9
10
11
12
private static async Task RunWithoutLua(IDatabase db)
{
var currentValue = await db.StringGetAsync(TestKey);

// Some complicated logic in C#
var newValue = Convert.ToInt32(currentValue) + 123;
await db.StringSetAsync(TestKey, newValue);

// Validate
var afterUpdatedValue = await db.StringGetAsync(TestKey);
Console.WriteLine($"RunWithoutLua: After updated value: {afterUpdatedValue}");
}

用 Lua 方式存取

這邊是示意才用這種方式寫,但大型專案還是建議把 .lua 整理起來分類放在資料夾內,這樣 repository 層比較乾淨,也不會有過多、複雜的邏輯。

閱讀全文 »

Docker-compose 設定檔注意事項 這篇文章的範例中,我們使用了 MongoDB 的容器。yml 如下:

1
2
3
4
5
6
7
8
version: '3'
services:
mongo-local-test.com:
image: mongo
container_name: mongodb_cotainer
ports:
- "27117:27017"
entrypoint: [ "/usr/bin/mongod","--bind_ip", "localhost,mongo-local-test.com"]

好處是每次開啟都是全新的,不用擔心資料弄髒弄壞,但如果想保留資料怎麼辦?這時候需要使用到 volumes 設定了。

1
2
volumes:
- '~/Desktop/mount-mongo-data:/data/db'

: 之前的是實體位置,之後的是 container 內的位置。

設定完後 docker-compose up / down 就可以看到實體檔案在該路徑上了。如下:

1
2
3
4
5
6
7
8
➜  mount-mongo-data ls
WiredTiger _mdb_catalog.wt index-1-6246240698095516127.wt mongod.lock
WiredTiger.lock collection-0-6246240698095516127.wt index-3-6246240698095516127.wt sizeStorer.wt
WiredTiger.turtle collection-2-6246240698095516127.wt index-5-6246240698095516127.wt storage.bson
WiredTiger.wt collection-4-6246240698095516127.wt index-6-6246240698095516127.wt
WiredTigerHS.wt diagnostic.data journal
➜ mount-mongo-data pwd
/Users/mingyi/Desktop/mount-mongo-data

總不可能每次都指定實體位置吧~這樣要維護也很困難,另一種方式就跟其他 container 的 voulume 一樣放置,語法如下:

1
2
3
4
5
6
7
8
9
10
11
12
version: '3'
volumes:
mongo-mount-data:

services:
mongo-local-test.com:
image: mongo:latest
container_name: mongo_latest
ports:
- "27117:27017"
volumes:
- mongo-mount-data:/data/db
閱讀全文 »

在使用 lua 的 unpack 語法一定要特別注意,如果已經知道參數的數量,那會強烈建議不要使用 unpack。

測試條件

  • 迴圈執行次數 一百萬
  • table 長度 5

選手一 unpack

1
2
3
4
5
local style1 = function()
for i=1, loopCount do
local v1,v2,v3,v4,v5 = table.unpack(table_a)
end
end

選手二 get value directly

1
2
3
4
5
6
7
8
9
local style2 = function()
for i=1, loopCount do
local v1 = table_a[1]
local v2 = table_a[2]
local v3 = table_a[3]
local v4 = table_a[4]
local v5 = table_a[5]
end
end

結果

1
2
elapsed: 75.28 ms  // style1: unpack
elapsed: 0.40 ms // style2

結論

閱讀全文 »

本文 sample code 可以改為 pass by function 方式更為簡潔,但之前文章就先不改動了。
可以參考這篇文章的寫法。


在專案中,對 redis 操作都已經改成全 lua 執行,因為是使用 redis 的關係,對此操作頻率肯定很高。前陣子經同事提醒,關於某些語法效能並不太好,因此對 for, pair, ipair 再次檢驗其效能。

測試條件

  • 迴圈執行次數 一百萬
  • table 長度 1000

選手一 for

1
2
3
4
5
for i=1, loopCount do
for i=1, #table_a do
x = table_a[i]
end
end

選手二 for with local variable length

1
2
3
4
5
6
for i=1, loopCount do
local leng = #table_a
for i=1, leng do
x = table_a[i]
end
end

選手三 pair

閱讀全文 »

在管理時,會需要監控哪些對 Redis 的操作比較慢(久),就有點像是 database 的 profiler 概念,會記錄所有慢的操作。

特別注意:

  • SLOWLOG 僅紀錄實際執行的時間,不包含來回請求或等待執行的時間
  • SLOWLOG 都是存在快取內,因此對效能影響很些微,可以放心在線上使用

設定 SLOWLOG 記錄門檻

SLOWLOG 有一個毫秒的門檻,超過就會被列入 SLOWLOG(default: 10ms)。要修改的話可以直接改 redis.conf 或是使用 config 指令。

執行時間超過 100ms:

1
CONFIG SET slowlog-log-slower-than 100

SLOWLOG 紀錄數量

SLOWLOG 最多需要保留幾筆(default:10筆),超過的話會先擠掉最舊的(FIFO)。

設定最多保留100筆

閱讀全文 »

結論:該使用什麼,沒有最正確,只有最適合。本篇文章選擇的方向,比較適合我在工作上遇到的情境,不見得看官也適用。

前提

冒險開始

很自然的,遇到時間,在C#階段我們會使用 DateTimeOffset,接著就很開心的存進 MongoDB。
打開MongoDB發現該欄位跟我們想得不太一樣,

alt

沒錯,DateTimeOffsetNow 變成一個陣列,裡面存的是 DateOffset,在sub document上加上 Index 絕對不是一件好事。

捨棄Offset!把它變成UTC DateTime格式吧!

哇~蒸蚌,這下欄位變成上圖的 DateTimeNow 欄位,沒有陣列了。
沒錯,這樣存進去就會變成只有一個欄位的 Date,但是這樣就夠了嗎?
經驗上這種時間格式一定都會拿來查詢條件,而查詢條件就是拿來比較的,介於~大小於這種,而這個比較的效率好嗎?肯定不會比數字來得好,就像比較字串效能還是輸給直接比較數字。

通通拿去轉Tick

閱讀全文 »