0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

私がサーバーのパフォーマンス向上のために使ったもの - ② ネットワークI/Oの最小化

Posted at

この内容は、担当しているプロジェクトによっては使用されないかもしれませんが、重要な概念であり、難しくもないため、ぜひ知っておいていただきたいと思います。

フロントエンドからのリクエストやレスポンスの過程を除けば、バックエンド開発者が最も頻繁に経験するネットワークI/Oは、おそらくデータベースへのアクセスでしょう。

まずネットワークI/Oを削減する方法を説明する前に、サーバーがフロントエンドのリクエストをどのように処理するのかを図で見てください。

図を見ると、クライアントからリクエストを受け取ったサーバーは、それに対するレスポンスを返しますが、その過程でビジネスロジックを実行するためにDBへアクセスする必要があります。このとき、ネットワークを通じてDBにクエリを送信します。

image.png

そして、サーバーがDBから大量のデータを受け取って処理するケースでなければ、ほとんどの場合、ビジネスロジックの処理時間よりも、サーバーとDB間でネットワークを通じてリクエストとレスポンスをやり取りする時間のほうが長くなります。

そのため、開発者は必要がない限り、ネットワークI/Oを最小限に抑える必要があります。

ここで、新人がよくやってしまうミスを例にとって、ネットワークI/Oを削減する方法を説明します。


例)

UserDtoList には複数のユーザー情報が格納されています。
accountDAO.signUpUser(userDto) メソッドは、MyBatis を使用してユーザー情報をDBに保存するメソッドです。

この UserDtoList の中から age が20歳を超えるユーザー情報をDBに保存するロジックを実装する場合、以下のようなコードになるかもしれません。

  public void insertAdult(List<UserDto> UserDtoList) {
    for (UserDto userDto : UserDtoList) {
      if (userDto.getUserAge() > 20) {
        accountDAO.signUpUser(userDto);
      }
    }
  }

一見、要件を満たしているように見えるこのコードですが、
もし age が20歳を超えるユーザーが100人いた場合、DBサーバーを100回呼び出すことになります。

image 1.png

前に述べたように、ネットワークを通じたリクエストとレスポンスのやり取りには時間がかかるため、これを最小限に抑える必要があります。

では、このコードをどのように改善すればよいのでしょうか?

結論から言うと、保存したいリストを一度にDBサーバーに送る方式を使います。

そのためには、上記のコードを2つのメソッドに分ける必要があります。

  1. List<UserDto> を受け取り、age > 20 のオブジェクトだけを抽出して返すメソッド → filterAdults(List<UserDto> userDtoList)
  2. List<UserDto> を受け取り、一度にDBへ保存するメソッド → accountDAO.signUpUserList(adults)

修正されたコードは以下のようになります。

    public void insertAdultUser(List<UserDto> userDtoList) {
        List<UserDto> adults = filterAdults(userDtoList);
        accountDAO.signUpUserList(adults);
    }

    private List<UserDto> filterAdults(List<UserDto> userDtoList) {
        List<UserDto> resultList = new ArrayList<>();
        for (UserDto userDto : userDtoList) {
            if (userDto.getUserAge() > 20) {
                resultList.add(userDto);
            }
        }
        return resultList;
    }

このように修正することで、DBサーバーを1回だけ呼び出して List を保存することができます。

大体のORMやSQLマッパーは List を使ったSQL処理をサポートしているため、それぞれのツールに合わせて実装すれば問題ありません。

なお、この方法は DBサーバーへの呼び出しを最小化し、ネットワーク通信にかかる時間を減らすためのものであって、ロジック自体の性能を向上させるものではない という点に注意してください。


まとめ

実は、Spring Bootで使用されているORMであるJPAは、クエリをメモリに蓄積しておいて、一括でDBにリクエストを送る仕組みを持っているため、あまり気にしなくても性能上の問題は発生しません。
(他のORMも同様かどうかは分かりませんが)

とはいえ、この記事の内容は基本として知っておくべきですし、ORMを使用しない場合もあります。そして何より、視野が広がるので知っておく価値があると思います。

最後まで読んでいただき、ありがとうございました。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?