[初心者向け]Webサービスを利用する際のDB接続とデータ保存について再入門

[初心者向け]Webサービスを利用する際のDB接続とデータ保存について再入門

おはようございます、もきゅりんです。

前回の続きです。

対象は、何となくAWSを触っているような、駆け出しITエンジニアさんです。IT業界で長く生息する方々には一般常識レベルのことが多いかと思います。

前回はHTTPプロトコルを使ってリクエスト/レスポンスを実行してみました。実際のWebサービスでは、HTTPのリクエストに応じてデータベースとやり取りします。

app-db

この記事での構成では、行指向型のリレーショナルデータベースを利用している、と想定します。

DBとの接続、および(DB)サーバとストレージの接続について簡単にまとめていこうと思います。

データベース接続とSQL処理

さて、例によって、DBアプリケーションは、ポートを開いてLISTEN状態で待機しています。 (この例では、RDSではなく、Amazon Linux2にmysql5.7.28をインストールして対応しています)

そのポートに対して、クライアントはTCP接続リクエストをします。

$ netstat -a
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State
tcp        0      0 0.0.0.0:sunrpc          0.0.0.0:*               LISTEN
tcp        0      0 0.0.0.0:ssh             0.0.0.0:*               LISTEN
tcp        0      0 localhost:smtp          0.0.0.0:*               LISTEN
tcp        0      0 ip-172-31-47-94.ap-:ssh g1-27-253-251-157:47900 ESTABLISHED
tcp        0    244 ip-172-31-47-94.ap-:ssh g1-27-253-251-186:43616 ESTABLISHED
tcp6       0      0 [::]:sunrpc             [::]:*                  LISTEN
tcp6       0      0 [::]:ssh                [::]:*                  LISTEN
tcp6       0      0 [::]:mysql              [::]:*                  LISTEN

何度かのやり取りが成功してTCPコネクションが確立されたら、ソケットを通してSQLコマンドによって、データのやり取りします。(DBが同一サーバで共存する場合、TCPを使わず、OSの機能で接続することが多いようです。)

$ netstat -a
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State
tcp        0      0 0.0.0.0:sunrpc          0.0.0.0:*               LISTEN
tcp        0      0 0.0.0.0:ssh             0.0.0.0:*               LISTEN
tcp        0      0 localhost:smtp          0.0.0.0:*               LISTEN
tcp        0      0 ip-172-31-47-94.ap-:ssh g1-27-253-251-157:47900 ESTABLISHED
tcp        0    244 ip-172-31-47-94.ap-:ssh g1-27-253-251-186:43616 ESTABLISHED
tcp6       0      0 [::]:sunrpc             [::]:*                  LISTEN
tcp6       0      0 [::]:ssh                [::]:*                  LISTEN
tcp6       0      0 [::]:mysql              [::]:*                  LISTEN
tcp6       0      0 ip-172-31-47-94.a:mysql ec2-3-112-130-173:33690 ESTABLISHED

クライアントとDBアプリケーションは、SQLコマンドを通じて、データをやり取りしますが、下図のような仕組みによって高速に処理されるようにしています。 *1

sql_flow

『松信(2012)p.139 図9-2を参考に作成』

クライアントからの接続を受けて専用のスレッドを割り当てて、切断時にはスレッドは次の接続に備えてコネククションプールに保管されます。

SQL文が過去に実行されたものと同一な場合、実行せずに事前にキャッシュされた検索結果を返却します。

キャッシュにヒットしない場合は、SQL文を解析し、文法チェックなどをして、SQL文の実行計画を作成します。この過程で,SQLが高速に実行できるように最適化も行います。

また高速に処理するためには、適切なデータモデリング、インデックスなどを用いてテーブルが設計されます。実際のデータ処理については、トランザクション *2も不可欠です。

SQL構文や実行については主題ではないため、省略します。

DBサーバとストレージについて

ところで、私のPCからリクエストされ、Webサービスを通して、データベースにSQLを通じて送信されたデータはどこに保存されているのでしょう。

当然、ストレージなのですが、

DBサーバとストレージはどのようにして接続されて、データを保存しているのでしょう。

下図の部分を見ていきます。

db_storage

ストレージとは

IT用語辞典では、下記のように記載されています。

ストレージ

別名: 二次記憶装置, 2次記憶装置
一般的には通電しなくても記憶内容が維持される記憶装置を指し、コンピュータが利用するプログラムやデータなどを長期間に渡って固定的に保存する用途に用いられる。コンピュータ内にはこれとは別に、半導体素子などでデータの記憶を行う主記憶装置(メインメモリ、メモリ)が内蔵されており、利用者がプログラムを起動してデータの加工を行う際にはストレージから必要なものをメモリに呼び出して使う。

ストレージの種類

ストレージの種類としては以下のようなものがあります。

  • HDD
  • SDD
  • USBメモリ
  • SDカード
  • 光学ディスク(CD,DVDなど)
  • 磁気テープ

ストレージを利用するに当たって、よく出てくる用語をまとめます。

よく出てくる用語

  • ランダムアクセスとシーケンシャルアクセス

ランダムアクセス(英: Random Access)とは、記憶装置などのデータへのアクセス方式のひとつで、端から順番にアクセスするというシーケンシャルアクセスに対して、何らかのアドレス付けによる番号などにより、目的のデータがある場所がわかっていれば、それを直接アクセスできる、というような方式である。 シーケンシャルアクセスでは通常、端から全部のデータにアクセスしつつ、目的の場所まで待たなければならないので、レイテンシが膨大になる。それに対しランダムアクセスではどの場所のデータにアクセスするのでも、一般に同じ待ち時間でアクセスできる。

  • ブロックデバイス

ブロックデバイス(またはブロックスペシャルファイル)は、ブロック形式でデータをやり取りする機器に対応している。ブロックデバイスは、HDD/CD-ROMドライブ/メモリ領域などのアドレス指定可能な機器を扱う。

  • パーティション

ハードディスクやSSDなどの記憶装置は、利用開始時に内部を複数のパーティションに分割し、それぞれをあたかも一台の独立したストレージのように取り扱うことができる。分割された区画のことをパーティションという。

  • ファイルシステム

OSの機能の一つで、永続的にデータを保存できる記憶装置(ストレージ/外部記憶装置)内部の記録状態を管理・制御し、人間に分かりやすいファイル単位でデータの書き込みや読み出しができるようにするシステム。ファイルシステムはディスクファイルシステム、分散ファイルシステム、特殊用途のファイルシステムがあるが、Linuxのディスクファイルシステムでは、ext2,ext3,ext4, XFSなどがあります。

  • マウント

ITの分野では、コンピュータ本体に接続した周辺機器をOSなどのソフトウェアに認識させ、操作・利用可能な状態にすることをマウントということがある。

ストレージの接続形態

さて、ストレージとサーバはどのように接続されているのでしょうか。ストレージとサーバの接続に利用されるのに、下記のようなインタフェースがあります。

ストレージとサーバーとの接続インタフェース

  • ATA (AT Attachment)
  • SATA (Serial ATA)
  • SCSI (Small Computer System Interface)
  • SAS (Serial Attached SCSI)
  • eSATA (External SATA)

ストレージの接続形態

ストレージの接続形態としては、下表のようになります。

ストレージの接続形態 ストレージとサーバを直接接続 | ネットワーク上の接続 プロトコル サーバの認識
DAS (Direct Attached Storage) 直接接続 ATA,SAS,SATA,SCSIなどを利用 ローカルストレージ
SAN (Storage Area Network) ネットワーク上の接続 iFCPやFCoE,iSCSIなどを利用 ローカルストレージ
NAS (Network Attached Storage) ネットワーク上の接続 NFSやCIFSといった上位プロトコルを利用 共有ファイルストレージ

あらゆるストレージ・アーキテクチャーが DAS、SAN、NAS のいずれかに分類されるわけではありません。(CAS (Content-Addressable Storage) といった技術もあります)

AWSのEBSがSANなのかNASなのかはハッキリしませんが、おそらく消去法的にSANの接続形態だと思われます。

ファイルシステムとVFS(Virtual File System)

下図が参考になります。

vfs

『出典:菅谷みどり (2003)「VFSとファイルシステムの基礎技術」@IT. https://www.atmarkit.co.jp/ait/articles/0305/20/news002_2.html, (2019/10/17)』

下記は引用です。

VFSはアプリケーションと記憶装置であるメディアの間に位置する。(略) つまり、VFSはどのアプリケーションからでも、同じような手順でファイル操作(媒体へのアクセス)が行えるような、共通のインターフェイスを提供している。

ここまでで、ストレージとサーバとの接続、アプリケーションとVFSを介したファイルシステムとのつながりの様子が見えてきました。

AWSで実際にストレージを利用する場合

参考として、下記を見ながらAmazon Linux2 AMI(ami-0ff21806645c5e492)のストレージを確認してみたいと思います。

Linux で Amazon EBS ボリュームを使用できるようにする

AWSでは、とりあえず何らかの接続形態でサーバにストレージを接続まではしてくれます。したがって、後はファイルシステムを作成してサーバにマウントするだけで問題ありません。(ルートボリュームは何もする必要ありません)

lsblkによって、/dev/xvdaにブロックデバイス(SDD)を確認できます。

$ lsblk
NAME    MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
xvda    202:0    0   8G  0 disk
└─xvda1 202:1    0   8G  0 part /
$ sudo file -s /dev/xvda1
/dev/xvda1: SGI XFS filesystem data (blksz 4096, inosz 512, v2 dirs)

RHEL7で標準ファイルシステムとされたSGIのXFSファイルシステムであることが分かります。

/etc/fstabファイルを確認すると、システムブート時にアタッチ済みのEBSボリュームをマウントする設定が確認できます。

$ cat /etc/fstab
#
UUID=xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx     /           xfs    defaults,noatime  1   1

マウントポイントがルート、ファイルシステムがXFS、推奨されるファイルシステムマウントオプションが記載されていることが分かります。

現在のディスク容量を調べます。

$ df -h
ファイルシス   サイズ  使用  残り 使用% マウント位置
devtmpfs         475M     0  475M    0% /dev
tmpfs            492M     0  492M    0% /dev/shm
tmpfs            492M  448K  492M    1% /run
tmpfs            492M     0  492M    0% /sys/fs/cgroup
/dev/xvda1       8.0G  2.5G  5.6G   31% /
tmpfs             99M     0   99M    0% /run/user/0
tmpfs             99M     0   99M    0% /run/user/1000

下記を参考にMySQLデータベースに適当にデータを大量に格納します。

MySQLで大量のテストデータを作成

テーブル行数を確認します。

mysql> select table_name, table_rows from information_schema.TABLES
    -> where table_schema = 'DBNAME';
+------------+------------+
| table_name | table_rows |
+------------+------------+
| sample     |    9722598 |
+------------+------------+
1 row in set (0.00 sec)

ディスク容量を確認します。(1G程度増えてます。)

$ df -h
ファイルシス   サイズ  使用  残り 使用% マウント位置
devtmpfs         475M     0  475M    0% /dev
tmpfs            492M     0  492M    0% /dev/shm
tmpfs            492M  424K  492M    1% /run
tmpfs            492M     0  492M    0% /sys/fs/cgroup
/dev/xvda1       8.0G  3.4G  4.7G   43% /
tmpfs             99M     0   99M    0% /run/user/1000

サーバを停止してから起動して確認します。(再起動ではありません)

当然ですが、データは無事保存されています。

最後に

復習も兼ねて、ブラックボックス感の強かったさまざまなものに少しだけ光を当てることができました。

まとめていて感じたのは、とにかく、ある機能と、ある機能とは密結合にならないように分離されて、何かしらのAPIを通じて処理できるように設計されている、ということでした。

すべてを理解することは不可能ですし、時間は有限なため、生産性を考慮しなければなりませんが、漠然と利用しているものの理解も少しずつ深めていけたらと思います。

そして思ったよりも、編集作業が大変でした。

以上です。

どなたかのお役に立ったら幸いです。

参考:

脚注

  1. 一般的には、すべてのRDBMSにクエリキャッシュやコネクションプールが必ずしも存在するわけではありません。あくまで一例です。
  2. トランザクション処理は、既知の一貫した状態のデータベースを維持するよう設計されており、相互依存のある複数の操作が全て完了するか、全てキャンセルされることを保証する。