2021 iThome 鐵人賽 MongoDB披荊斬棘之路


DAY2 安裝與使用 MongoDB

MongoDB 在社群, 非營利環境使用是免費的,而我一律推薦使用 docker 來操作,一來省掉一堆平台相容性以及安裝上遇到的問題,二來大家都會是一樣的起始狀態,三來是弄壞了也不用擔心重裝麻煩之類的。所以本篇以下不會介紹到任何os獨有安裝方式,僅使用 docker 而已,如果對 docker 熟稔的朋友就可以直接跳過本篇了。

安裝

Application 安裝模式會因為各種os有所不同,這邊不會一一介紹,有需要的可以直接參考官網 各個平台安裝 MongoDB 方法。我要用的是以 docker 方式來啟動 MongoDB。(開始前請先安裝 docker)

首先直接從 docker hub 拉取最新的映像檔(image)

docker pull mongo:latest

有指定的版本號或 tag 也可以去 docker hub 查看

1
2
3
4
5
6
7
8
➜  / docker pull mongo:latest
latest: Pulling from library/mongo
16ec32c2132b: Already exists
81447d2c233f: Pull complete
Digest: sha256:93ea50c5f15f9814870b3509449d327c5bc4d38f2b17c20acec528472811a723
Status: Downloaded newer image for mongo:latest
docker.io/library/mongo:latest
➜ /
閱讀全文 »

2021 iThome 鐵人賽 MongoDB披荊斬棘之路


DAY1 揭開序幕與 MongoDB 簡介

前言

終於鼓起勇氣要報名 iThome 鐵人賽!
本系列將會陸續介紹與紀錄在使用 MongoDB 的一些心得以及雷區。MongoDB 在 2021 已經是家喻戶曉的資料庫了,網路上教學資源也很多,我會分配多一些內容在中文資源較少的部分,讓大家能夠更容易學習到更多不同面向的 MongoDB,如有錯誤或者問題請不吝指教。

使用環境

我使用的環境是

  • macOS
  • .NET Core
  • Docker

要如何使用會簡單的說明,因為主軸畢竟還是在 MongoDB。

MongoDB 簡介

閱讀全文 »

2021 iThome 鐵人賽 MongoDB披荊斬棘之路


Basic

Advanced


閱讀全文 »

這篇文章會提到關於 Redis cluster 的內容,如果不太清楚還是建議先去了解一下。


今天遇到一個狀況,當我們在使用 redis cluster 時,按照官網的範例通常 key 會是這樣的格式 IAMKEY{SLOT},那如果將 slot 位置改變到 key 中間,算出來的 hash slot 會不同嗎?


實驗開始

Step 1 啟動 Redis

先啟用 redis (看你是要使用 instance 還是 docker)
我比較習慣把常用的服務寫在一個 docker-compose yml 內,這樣每次都能用到全新的,也不用各別一一啟動。

1
2
3
4
5
6
7
8
9
version: "3.7"
services:
redis:
image: redis
container_name: redis-testdc
ports:
- "16377:6379"
environment:
PASSWORD: pass.123

Step 2 連上 redis

閱讀全文 »

本篇資料庫的資料依舊使用上一篇文章的範例,沒有的可以新增一下。

1
2
3
4
5
6
7
db.getCollection('movie').insertMany([
{"name": "movieA", "language": "en-gb", "rating": 8, "totalCost": 30000000, "producer": "companyA"},
{"name": "movieB", "language": "en-gb", "rating": 5, "totalCost": 10000000, "producer": "companyA"},
{"name": "movieC", "language": "zh-tw", "rating": 6, "totalCost": 25000000, "producer": "companyA"},
{"name": "movieD", "language": "zh-tw", "rating": 8, "totalCost": 10000000, "producer": "companyB"},
{"name": "movieE", "language": "zh-tw", "rating": 9, "totalCost": 6000000, "producer": "companyC"},
])

$count

這邊要介紹的 $count 是在 aggregation 階段使用的,跟在 5.0 後 group 內使用的又有點語法不同。

  • Aggregation 版本
1
2
3
4
db.movie.aggregate([
{ $match: { rating: { $gt: 7 } } },
{ $count: "popular_movie" }
])
  • Group 版本 (5.0之前)
1
2
3
4
5
db.movie.aggregate( [
{ $match: { rating: { $gt: 7 } } },
{ $group: { _id: null, rating: { $sum: 1 } } },
{ $project: { _id: 0 } }
] )
  • Group 版本 (5.0之後)
閱讀全文 »

其實 Git 取消使用密碼登入的通知已經發了一年多了,只是我一直選擇性忽略掉它。直到 2021/08/13 它失效那一天,我無法再部署那一天,我才不甘願地去處理這件事。其實不是一件很困難的事情,但還是簡單紀錄下,因為還是花點時間看了一下怎麼調整。


警告訊息長這樣

在使用 Hexo 部署 Git 時候,總是會跳這個警告

1
2
3
4
5
6
7
8
9
10
11
12
13
14
remote: Support for password authentication was removed on August 13, 2021. Please use a personal access token instead.
remote: Please see https://github.blog/2020-12-15-token-authentication-requirements-for-git-operations/ for more information.
fatal: unable to access 'https://github.com/myctw/myctw.github.io/': The requested URL returned error: 403
FATAL Something's wrong. Maybe you can find the solution here: http://hexo.io/docs/troubleshooting.html
Error: remote: Support for password authentication was removed on August 13, 2021. Please use a personal access token instead.
remote: Please see https://github.blog/2020-12-15-token-authentication-requirements-for-git-operations/ for more information.
fatal: unable to access 'https://github.com/myctw/myctw.github.io/': The requested URL returned error: 403

at ChildProcess.<anonymous> (/Users/mingyi/Documents/blog/node_modules/hexo-util/lib/spawn.js:37:17)
at ChildProcess.emit (events.js:182:13)
at maybeClose (internal/child_process.js:962:16)
at Socket.stream.socket.on (internal/child_process.js:381:11)
at Socket.emit (events.js:182:13)
at Pipe._handle.close (net.js:606:12)

其實第一句就告訴你了,使用密碼驗證的方式要移除了,建議使用 personal access token


Step1 建立 personal access token

  1. Git 右上角個人 Icon 點下去,找到 settings
    alt
  2. 左邊一排設定中找到 Developer settings
    alt
  3. 左邊一排設定中找到 Personal access tokens
  4. 點選 Generate new token
    alt
  5. Note 欄位就是名稱,基本上有個意義、看得出來是幹嘛的就好
  6. Expiration 強烈建議不要改為永久,因為這就不是 token 了,我是設定一個月啦~怕自己忘記這個操作,乾脆頻繁來使用。
  7. 權限部分就看各位的需求自己設定了,我的目的是只有使用 blog,那麼權限就不應該這麼大
    alt
  8. 接著點選 Generate Token 就可以囉!要記得 token 只會顯示一次,也就是現在,之後只能重新產生了喔!所以一定要先複製好。

Step2 修改 MacOS keychain

閱讀全文 »

大家可以想一下什麼情況下會需要使用 memory cache?

我自己的話,一些比較不常變動的設定值,可以放在記憶體內做快取,定期地去讓它失效,再重新拿 database 的值放回快取更新。

這篇介紹的注意事項有一些特定狀況,並不是所有人都會碰到。這樣寫的情境乍看很像對,但測試起來又不太對,我想應該是對於內建方法GetOrCreate有些誤解。

下面這段程式:

  1. 傳入 string key 希望取得 value
  2. 若 key 值為 key1 則回傳 111,若為其他則會發生錯誤,並且回傳預設值 999。這邊是模擬用其他 key 值查詢 cache 時,遇到不可預期的錯誤。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public static class MemoryCacheHelper
{
private static readonly IMemoryCache _memoryCache = new MemoryCache(new MemoryCacheOptions());

public static async Task<int> GetCacheValue(string key)
{
return await _memoryCache.GetOrCreateAsync(key, async data =>
{
try
{
// Get kv data from database
if (key == "key1") return await Task.FromResult(111);

throw new Exception("Unexpected error");
}
catch (Exception)
{
// log error
}
return 999;
}
);
}
}

第一個測試中,傳入 key1,預期會收到 111的結果。
第二個測試,傳入 keyX,模擬取值遇到例外狀況,然後回傳預設值。這次錯誤可以先略過,並且回傳預設值,那麼下一次呼叫快取呢?如果發現快取是空的,那麼理所當然又要去資料庫撈一次。

這時候問題就來了,第二次測試的 key 值 keyX 其實已經存在在 memory cache 內了,所以第三個測試,再次傳入 keyX 只會取得 999,但這個 999 並非來自例外狀況的預設回傳值且不再會 reload database 的資料,直到 keyX/999 這組 kv 自然失效(expire)。

實際執行結果..

1
2
3
4
5
6
7
var key1Value = await MemoryCacheHelper.GetCacheValue("key1");
var keyXValue = await MemoryCacheHelper.GetCacheValue("keyX");
var keyXValue_2ndCall = await MemoryCacheHelper.GetCacheValue("keyX");

Console.WriteLine($"key1_value:{key1Value}");
Console.WriteLine($"keyX_value:{keyXValue}");
Console.WriteLine($"keyX_Value_2ndCall:{keyXValue_2ndCall}");
閱讀全文 »

這篇想來記錄一下 Redis expire 相關指令的用法與意義,有時候時間一長就忘了,自我救助金魚腦。比較深入的 TTL 機制就放到後面再說..

EXPIRE

  • 精確度為 Second(秒級)
  • 替 key 設定存活時間,設定後就無法更改。此失效時間為 TTL (Time to live),單位是秒,也就是還能存活幾秒的意思
  • 可以使用 persist 指令移除失效時間
  • 官方宣稱 2.6 版後的誤差會在 1 ms 內,但我想還是要看當時的忙碌程度。
  • 回傳 1,表示設定成功
  • 回傳 0,表示該 key 不存在

相關指令:

  • TTL (查詢剩餘存活時間)
  • PEXPIRE (精確度為 Millisecond)

EXPIREAT

  • 精確度為 Second(秒級)
  • 替 key 設定失效時間,設定後就無法更改。此時間為 Unix timestamp,時間到了通常會在1秒內被刪除

相關指令:

  • EXPIRETIME (查詢剩餘存活時間)
  • PEXPIRETIME (精確度為 Millisecond)
1
2
3
4
5
6
7
redis> SET key1 value1
"OK"
redis> EXPIREAT key1 1627885364
(integer) 1
redis> EXPIREAT key1 1627885364
(integer) 0
redis>
閱讀全文 »

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;
}
閱讀全文 »