定期的にまとめたい、技術系のおはなし。
スマホアプリの実績集計のデータ蓄積にMongoDB×Javaを使用していて、いろいろなことに遭遇しました。備忘録がてら、どのようなことが起きて、対処したかをまとめました。
根本的な解決にならず、一時的な対処方法である方法もあるかもしれません。 時間が経過して対応方法が変わったりしたときは、この記事に追記します。
また英語翻訳はGoogle先生にお任せしています。
MongoDB×Java
対応している諸々のバージョンとか
以前の記事でお話した環境で開発しています。まったくかわっていないのが、情報乗っけておきますね。
- Java 1.8
- Spring Framework 4.3.4
- MyBatis 3.3.0
- JSONIC 1.3.10
- MySQL5.7
- MongoDB 3.4
以降、いろいろ経験していて
MongoDBの接続が断続的になる
アプリを使用してるユーザーが多くなり、MongoDBへの読み書きが多くなってきたころ、アプリロジック上は問題ないはずなのに、MongoDBの接続が断続的になってしまう現象が発生。
Java内で接続している箇所は以下のような実装していました。
※メソッドわけだったり、implementsだったり、いろいろ省略していますが、だいたいこんなかんじ。
public class MovieInfoService {
/**
* MongoClient
*/
private MongoClient mongoClient;
public MongoDatabase mongoSample() {
// Mongoクライアントオブジェクト取得 []内は環境設定
this.mongoClient = new MongoClient(
new MongoClientURI("mongodb://" + [hostPort] + "/?replicaSet=" + [replicaSetName]));
// MongoDBに対する処理
// Mongoクライアントクローズ
this.mongoClient.close();
}
}
原因:MongoDBClientインスタンスを接続するたびにclose()していた
色々調べてみたんだけど、MongoDBClientインスタンスをクローズしていて、インスタンスを失くしてしまうことが悪いっぽい。
だったり。
以下、MongoDB JavaDriver 接続方法 の引用。
Typically you only create one MongoClient instance for a given MongoDB deployment (e.g. standalone, replica set, or a sharded cluster) and use it across your application. However, if you do create multiple instances:
All resource usage limits (e.g. max connections, etc.) apply per MongoClient instance.
To dispose of an instance, call MongoClient.close() to clean up resources.
訳
通常は、特定のMongoDBデプロイメント用に1つのMongoClientインスタンス(スタンドアロン、レプリカセット、またはシャードされたクラスタなど)を作成し、アプリケーション全体で使用します。ただし、複数のインスタンスを作成する場合は、
MongoClientインスタンスごとに、すべてのリソース使用制限(最大接続など)が適用されます。
インスタンスを破棄するには、MongoClient.close()を呼び出してリソースをクリーンアップします。
1つのMongoClientインスタンスで使いまわせってめっちゃ書いてるのをね、見落としてたんですよね……。
対策1:MongoClientインスタンス
新しい接続のときはMongoClientインスタンスを作成させて、既にあれば作成しないようにします。
また、むやみにクローズさせないようにします。
実装の方はカンタン。1文追加。close()メソッドは呼び出さないようにします。
public class MovieInfoService {
/**
* MongoClient
*/
private MongoClient mongoClient;
public MongoDatabase mongoSample() {
// Mongoクライアントオブジェクト取得 []内は環境設定
if(this.mongoClient==null)
this.mongoClient = new MongoClient(
new MongoClientURI("mongodb://" + [hostPort] + "/?replicaSet=" + [replicaSetName]));
// MongoDBに対する処理
// Mongoクライアントクローズ
this.mongoClient.close();
}
}
対策2:接続するmaxPoolSizeの調整
MongoDBに接続する際、接続プール内の最大接続数を設定することができます。アプリケーションによっては調整が必要になってくる箇所だと思う。
public class MovieInfoService {
/**
* MongoClient
*/
private MongoClient mongoClient;
public MongoDatabase mongoSample() {
// Mongoクライアントオブジェクト取得 []内は環境設定
if(this.mongoClient==null)
this.mongoClient = new MongoClient(
new MongoClientURI("mongodb://" + hostPort + "/?replicaSet=" + replicaSetName+"&maxPoolSize="+[maxPoolSize]));
// MongoDBに対する処理
// Mongoクライアントクローズ
this.mongoClient.close();
}
}
こちらは検証した結果、既存のアプリでは結果的にはデフォルトの値(100)で問題ないことになりました。
aggregateでMongoCommandExceptionエラー
MongoDBに蓄積しているデータをaggregateを使って集計したりしているのですが、前日まで正常に動いていたのに、MongoCommandExceptionエラーが発生。
MongoCommandExceptionエラーのメッセージが以下のとおり。
Command failed with error 16819: ‘Sort exceeded memory limit of 104857600 bytes, but did not opt in to external sorting. Aborting operation. Pass allowDiskUse:true to opt in.’ on server ip-127-0-0-1.localdomain:27017.
The full response is { “ok” : 0.0, “errmsg” : “Sort exceeded memory limit of
104857600 bytes, but did not opt in to external sorting. Aborting operation.
Pass allowDiskUse:true to opt in.”, “code” : 16819, “codeName” :
“Location16819” }
原因:aggregate処理で取得したデータ量が大きかった
通常のMongoDBへの接続だと一時ファイルへの書き込みをせずaggregate処理を行う。
その取得したデータをソートしているのだが、そのデータの上限が104857600 バイト、つまり約100MBとのこと。
対策:aggregate処理にallowDiskUseオプションを使用する
aggregate処理のとき、一時ファイルへの書き込みを有効にできるallowDiskUseオプションを使用して接続する。
db.sampleCollection.aggregate(
[
{aggregateの処理}
],
{allowDiskUse: true}
);
JavaだとgetCollection().aggregateメソッドを使用するときにallowDiskUseオプション使用することになる。たとえば、下のような感じ。
Listpipeline = new ArrayList (); // pipelineの処理作成 //MongoDB接続後、allowDiskUseをtrueにして接続する AggregateIterable iterable = getCollection().aggregate(pipeline).allowDiskUse(true); // aggregate処理で取得した値の処理
一時ファイルへの書き込みを許可することになるので、処理を行っているサーバーのHDDの容量には注意が必要です。
HHDの容量があまりにも小さい場合は、容量増加の検討をしてみてください。
まとめ
参考記事などが英語が多く、ノウハウが落ちていることが少ないMongoDB×Java。
なにかの参考になれば幸いです。
MongoDB×Javaのアプリケーション開発で立ち上げたときに苦労したことだったり、いろいろまとめたのがあるので、そちらもよければどうぞ。