LiteDBとSQLiteのパフォーマンスを計測してみた(C#)

# はじめに

ソーシャルゲームのローカルリソースバージョンを管理する際にLiteDBとSQLiteどちらが高速なのか気になったので、ベンチマークしてみることにしました。

ソーシャルゲームのリソースは運用が長くなると数が多くなり、管理するレコード数も増加していきます。

主に管理するデータはダウンロード済みかどうかとダウンロード済みの場合最新のリソースとバージョンがあっているかを確認するためのハッシュ値です。

アプリ起動時にすべてのリソースのバージョン確認、ダウンロードが一つ完了するたびにバージョン情報の更新がはいるので速度が重要になってきます。

今回の検証で使用したプロジェクトはGitHubにあげておきました。

https://github.com/k-okawa/LocalDBBenchmark/tree/master

レコード数が100,10000,100000のケースで計測してみます。

Meanが平均時間になるのでこちらが一番重要な結果です。

# 使用パッケージ
## ベンチマーク

https://github.com/dotnet/BenchmarkDotNet

## LiteDB

https://github.com/mbdavid/litedb

## SQLiteNet

https://github.com/praeclarum/sqlite-net

# [InsertBulk(一括挿入)](https://github.com/k-okawa/LocalDBBenchmark/blob/master/DBBenchmark/DBInsertBenchmark.cs)
|       Method | insertCount |        Mean |     Error |     StdDev |      Median |         Min |         Max |
|------------- |------------ |------------:|----------:|-----------:|------------:|------------:|------------:|
| LiteDBInsert |         100 |    12.98 ms |  0.457 ms |   1.333 ms |    12.50 ms |    10.63 ms |    16.48 ms |
| SQLiteInsert |         100 |    18.56 ms |  0.440 ms |   1.270 ms |    18.35 ms |    16.54 ms |    22.15 ms |
| LiteDBInsert |       10000 |   325.93 ms |  6.463 ms |  10.975 ms |   326.41 ms |   296.67 ms |   342.32 ms |
| SQLiteInsert |       10000 |    84.65 ms |  6.556 ms |  18.706 ms |    79.68 ms |    60.74 ms |   140.95 ms |
| LiteDBInsert |      100000 | 4,613.43 ms | 91.210 ms | 235.442 ms | 4,543.66 ms | 4,176.39 ms | 5,163.66 ms |
| SQLiteInsert |      100000 |   615.54 ms | 12.134 ms |   9.473 ms |   615.57 ms |   599.93 ms |   629.06 ms |

# [GetAllRecords(全レコード取得)](https://github.com/k-okawa/LocalDBBenchmark/blob/master/DBBenchmark/DBGetCollectionBenchmark.cs)
100レコードの時の単位がμsと小さかったため、別々に計測結果を出しました。
## 100レコード
|              Method |     Mean |    Error |   StdDev |      Min |      Max |
|-------------------- |---------:|---------:|---------:|---------:|---------:|
| LiteDBGetCollection | 901.8 us | 17.47 us | 18.69 us | 874.2 us | 946.9 us |
| SQLiteGetCollection | 659.4 us | 13.05 us | 28.08 us | 618.0 us | 741.3 us |

## 10,000レコード
|              Method |     Mean |    Error |   StdDev |      Min |      Max |
|-------------------- |---------:|---------:|---------:|---------:|---------:|
| LiteDBGetCollection | 33.45 ms | 0.418 ms | 0.391 ms | 32.57 ms | 33.98 ms |
| SQLiteGetCollection | 13.90 ms | 0.277 ms | 0.647 ms | 12.99 ms | 15.66 ms |

## 100,000レコード
|              Method |     Mean |   Error |  StdDev |      Min |      Max |
|-------------------- |---------:|--------:|--------:|---------:|---------:|
| LiteDBGetCollection | 375.1 ms | 6.99 ms | 6.54 ms | 367.0 ms | 386.0 ms |
| SQLiteGetCollection | 143.7 ms | 2.18 ms | 1.93 ms | 140.7 ms | 147.8 ms |

# [Find&Update(レコード更新)](https://github.com/k-okawa/LocalDBBenchmark/blob/master/DBBenchmark/DBUpdateBenchmark.cs)
|                 Method | insertCount |     Mean |    Error |   StdDev |   Median |       Min |      Max |
|----------------------- |------------ |---------:|---------:|---------:|---------:|----------:|---------:|
| LiteDBUpdateCollection |         100 | 11.70 ms | 0.360 ms | 0.956 ms | 11.66 ms |  9.308 ms | 14.33 ms |
| SQLiteUpdateCollection |         100 | 13.99 ms | 0.461 ms | 1.238 ms | 13.75 ms | 11.749 ms | 18.25 ms |
| LiteDBUpdateCollection |       10000 | 11.37 ms | 0.243 ms | 0.648 ms | 11.47 ms |  9.852 ms | 13.18 ms |
| SQLiteUpdateCollection |       10000 | 15.46 ms | 0.893 ms | 2.549 ms | 14.58 ms | 12.668 ms | 22.13 ms |
| LiteDBUpdateCollection |      100000 | 12.70 ms | 0.492 ms | 1.370 ms | 12.38 ms | 10.353 ms | 16.77 ms |
| SQLiteUpdateCollection |      100000 | 14.09 ms | 0.479 ms | 1.302 ms | 13.82 ms | 11.359 ms | 18.18 ms |

# 考察
検証する前はLiteDBのほうが高速かと思っていましたが、大量のレコードに対してはSQLiteのほうが高速のようです。
大量のレコードのInsertはSQLiteが圧勝しています。
全レコード一括取得に関してはどのレコード数でもSQLiteが勝っていますが、100レコードの場合はどちらもμsなのでゲームアプリ上ではあまり問題にならない速度です。
Updateに関してはLiteDBのほうが高速ですがあまり差はありません。
トータルで見ると少ないレコード数はLiteDB、大量のレコード数の場合はSQLiteを使用するのがよさそうです。
また、LiteDBはNoSQLで、RDBのSQLiteより柔軟な形でデータを保存することができるためローカルセーブに向いている思います。
(ソーシャルゲームでゲームに関するデータを大量にローカルに持つことはないので)

# 最後に
初めてBenchmarkDotNetを使用して計測したため、正確な計測結果ではないかもしれませんがご了承ください。

Unityの場合、ローカルセーブには[EasySave](https://assetstore.unity.com/packages/tools/utilities/easy-save-the-complete-save-data-serialization-system-768?locale=ja-JP)というアセットがあるのでパフォーマンスの面ではLiteDBを使用するよりこちらのほうが高速かもしれません。
ソーシャルゲームでよく利用されるReadOnlyのマスタデータに関しては[MasterMemory](https://github.com/Cysharp/MasterMemory)で管理するのが良いと思います。

ローカルリソースのバージョン管理にSQLiteを使用するのはどうかなと思っていましたが、今回の検証結果からSQLiteを使用するのは割と良い選択だと思いました。