文章目錄
[Benchmark] 使用 C# 對 NoSQL insert 操作的效能數據
最近專案需要將收到的原始 request 內容直接儲存下來,以備日後有問題或是後續加工使用。
針對這類只有 insert 跟 select 操作的需要,過去大多透過 MongoDB 做為儲存媒介,但幾次使用下來對於 c# 操作 MongoDB 的 api 始終覺得不太順手,加上後來陸續看了些效能比較的數據, MongoDB 似乎沒有佔到優勢,所以想趁著這次機會試用 Cassandra , PostgreSQL , ArangoDB , RavenDB , CouchDB ,不過想要說服自己或是團隊改用其他技術最重要的莫過於執行效率了,於是老話一句:天下武功唯快不破,就直接來看看模擬實際情境透過 c# 來進行 insert data 的效能數據吧
因為這次開發的系統目標 concurrent user 及操作量很大,所以可以預見 log 數量也會有相當的級別,因此想要先測試 insert 的效能,先扛得住寫入再來想後續讀取的問題
基本環境說明
- macOS Mojave 10.14.2
- intel i5 2.3G
- 16 GB 2133 MHz Ram
- 各 NoSQL 皆使用官方最新版 (lastest) docker image 建立一個 node
- MongoDB 4.0.6
- Cassandra 3.11.3
- PostgreSQL 11.2
- ArangoDB 3.4.2
- RavenDB 4.1
- CouchDB 2.3.0
- .NET Core 2.2.101
- Bogus 25.0.4
- BenchmarkDotNet 0.11.4
驗證用 data poco
class User { public Guid UserId { get; set; } public string Name { get; set; } public DateTime Birthday { get; set; } public decimal CurrentSalary { get; set; } }
Bogus 套件製造假資料
User GetFakeUserData() { var fakesUsers = new Faker<User>() .RuleFor(a => a.UserId, f => f.Random.Guid()) .RuleFor(a => a.Name, f => f.Name.FirstName()) .RuleFor(a => a.Birthday, f => f.Date.Past()) .RuleFor(a => a.CurrentSalary, f => f.Random.Decimal(0M, 10,000M)) .Generate(); return fakesUsers; }
NoSQL 測試語法
MongoDB
環境建立流程及使用可以參考 使用 C# 存取 MongoDB
Insert 語法
var client = new MongoClient(); var db = client.GetDatabase("benchmark"); var collection = db.GetCollection<User>("users"); var fakesUsers = UserUtility.GetFakeUsers(Times); await collection.InsertManyAsync(fakesUsers);
數據
Method Times Mean Error StdDev Median MongoDbInsert 100 4.824 ms 0.2087 ms 0.6087 ms 4.648 ms MongoDbInsert 10,000 111.686 ms 2.2250 ms 2.3807 ms 111.603 ms MongoDbInsert 1,000,000 10,725.614 ms 65.2694 ms 61.0531 ms 10,727.612 ms
Cassandra
環境建立流程及使用可以參考 使用 C# 存取 Cassandra
Insert 語法
var cluster = Cluster.Builder() .AddContactPoints("127.0.0.1") .WithPort(9042) .Build(); var session = cluster.Connect("benchmark"); var userttemplate = session.Prepare("INSERT INTO users (userid, name, birthday, currentsalary) VALUES (?, ?, ?, ?)"); foreach (var userList in UserUtility.SpiltBySize(UserUtility.GetFakeUsers(Times), 100)) { var batch = new BatchStatement(); foreach (var _user in userList) { batch.Add(userttemplate.Bind(_user.UserId, _user.Name, _user.Birthday, _user.CurrentSalary)); } session.Execute(batch); }
數據
Method Times Mean Error StdDev CassandraInsert 100 37.53 ms 2.047 ms 6.004 ms CassandraInsert 10,000 1,633.61 ms 33.614 ms 97.519 ms CassandraInsert 1,000,000 165,795.11 ms 3,301.669 ms 4,293.102 ms
CouchDB
環境建立流程及使用可以參考 使用 C# 存取 CouchDB
Insert 語法
var connectionString = "http://localhost:5984/"; var client = new CouchClient(connectionString); var db = await client.GetDatabaseAsync("benchmark"); var fakesUsers = UserUtility.GetFakeUsers(Times); foreach (var userList in UserUtility.SpiltBySize(fakesUsers, 10,000)) { await db.BulkInsertAsync(userList.ToArray()); }
數據
Method Times Mean Error StdDev InsertTest 100 36.62 ms 0.7554 ms 2.131 ms InsertTest 10,000 1,628.92 ms 26.8867 ms 25.150 ms InsertTest 1,000,000 174,499.33 ms 2,222.5798 ms 1,970.259 ms
RavenDB
環境建立流程及使用可以參考 使用 C# 存取 RavenDB
Insert 語法
var store = new DocumentStore { Urls = new string[] {"http://localhost:8080"}, }; store.Initialize(); var fakeuserUSers = UserUtility.GetFakeUsers(Times); using (BulkInsertOperation bulkInsert = store.BulkInsert("benchmark")) { foreach (var user in fakeuserUSers) { bulkInsert.Store(user); } }
數據
Method Times Mean Error StdDev InsertTest 100 44.04 ms 1.477 ms 4.332 ms InsertTest 10,000 1,066.53 ms 21.480 ms 43.877 ms InsertTest 1,000,000 102,426.60 ms 1,157.901 ms 966.900 ms
ArangoDB
環境建立流程及使用可以參考 使用 C# 存取 ArangoDB
Insert 語法
ArangoDatabase.ChangeSetting(s => { s.Database = "benchmark"; s.Url = "http://localhost:8529"; s.Credential = new NetworkCredential("root", "pass.123"); s.SystemDatabaseCredential = new NetworkCredential("root", "pass.123"); }); var fakesUsers = UserUtility.GetFakeUsers(Times); using (var db = ArangoDatabase.CreateWithSetting()) { foreach (var userList in UserUtility.SpiltBySize(fakesUsers, 10,000)) { db.Collection("Users").InsertMultiple(userList); } }
數據
Method Times Mean Error StdDev Median InsertTest 100 15.24 ms 1.369 ms 3.927 ms 14.02 ms InsertTest 10,000 696.75 ms 13.726 ms 28.652 ms 692.08 ms InsertTest 1,000,000 NA NA NA NA
PostgreSQL
環境建立流程及使用可以參考 使用 C# 存取 PostgreSQL
Insert 語法
string connString = "Server=127.0.0.1; User Id=postgres; Database=benchmark; Port=5432; Password=pass.123; SSL Mode=Prefer; Trust Server Certificate=true"; var fakeuserUSers = UserUtility.GetFakeUsers(Times).Select(a=>JsonConvert.SerializeObject(a)); using (var conn = new NpgsqlConnection(connString)) { conn.Open(); using (var writer = conn.BeginBinaryImport( "COPY test.users (\"User\") FROM STDIN (FORMAT BINARY)")) { foreach (var user in fakeuserUSers) { writer.StartRow(); writer.Write(user, NpgsqlDbType.Json); } writer.Complete(); } }
數據
Method Times Mean Error StdDev Median InsertTest 100 6.501 ms 0.2907 ms 0.8386 ms 6.256 ms InsertTest 10,000 88.128 ms 2.6015 ms 6.8987 ms 86.356 ms InsertTest 1,000,000 7,523.930 ms 147.6126 ms 220.9395 ms 7,540.428 ms
實際數據比較
100
Method Mean MongoDB 4.824 ms Cassandra 37.53 ms CouchDB 36.62 ms RavenDB 44.04 ms ArangoDB 15.24 ms PostgreSQL- json 6.501 ms 10,000
Method Mean MongoDB 111.686 ms Cassandra 1,633.61 ms CouchDB 1,628.92 ms RavenDB 1,066.53 ms ArangoDB 696.75 ms PostgreSQL -json 88.128 ms 1,000,000
Method Mean MongoDB 10,725.614 ms Cassandra 165,795.11 ms CouchDB 174,499.33 ms RavenDB 102,426.60 ms ArangoDB NA PostgreSQL -json 7,523.930 ms
心得
為了選擇 NoSQL ,事前查過網路上的相關比較:Cassandra vs. MongoDB vs. Couchbase vs. HBase 與 NoSQL Performance Benchmark 2018: MongoDB, PostgreSQL, OrientDB, Neo4j and ArangoDB,就相關文獻看來選擇 Cassandra 的機會相對較高,因此我們確實也使用 Cassandra 完成了 POC,但 POC 過程中發現 .NET client 有一些缺陷,其中最嚴重的幾個大資料操作 API:CQLSSTableWriter、sstableloader 並沒有實作,讓實際透過 C# 操作 Cassandra 時效能並不如宣稱中的優異
另外 ArangoDB 在中量資料數時表現並不差,只是在百萬筆資料的測試情境一直無法完成測試(已嘗試拆為幾個小 batch),較為可惜
反而是 MongoDB 不愧是最受歡迎的 document 類型 NoSQL,效能表現非常令人讚賞,但最令人驚豔的就屬 PostgreSQL ,以 RDBMS 之姿在 json insert 的操作上竟打敗其他 NoSQL ,實在太強大了
關於這次我堅持自行測試效能數據,我認為團隊應該使用自己熟悉的語言、環境、工具來進行 POC,便可以儘早反應出實際使用上的現象,以本次測試為例:datasax 跑出的 Cassandra 數據完全碾壓 MongoDB 但加上語言及 library 因素後結果卻完全不同
2019-03-01 補充
以測試結果來看,PostgreSQL 的 insert 數據是最好的,但該使用哪套技術則不是單純由數據來可以下定論的,以我所在團隊為例:雖然知道 PostgreSQL 在 insert json 的速度最快,但最後選了第二名的 MongoDB, 主要是團隊多數成員都有 MongoDB 使用經驗,對於 PostgreSQL 則幾乎沒有,更遑論 PostgreSQL 的調校、HA 機制架設能力
參考資料
文章作者 Yowko Tsai
上次更新 2021-11-02
授權合約
本部落格 (Yowko's Notes) 所有的文章內容(包含圖片),任何轉載行為,必須通知並獲本部落格作者 (Yowko Tsai) 的同意始得轉載,且轉載皆須註明出處與作者。
Yowko's Notes 由 Yowko Tsai 製作,以創用CC 姓名標示-非商業性-相同方式分享 3.0 台灣 授權條款 釋出。