PHPスクリプトからデータベース接続時に行われていること

#1 はじめに
今年はデータベースを使うことが多くなりそうなので、ネットワークの視点から
データベースに接続する際の下記1行でどんな処理をしているのかをしりたかった。
`$pdo = new PDO('mysql:host=127.0.0.1;dbname=testdb', 'root', 'admin');`
たいした話ではない。。。

#2 環境
VMware Workstation 15 Player上の仮想マシン(1台)を使いました。
仮想マシンのOS版数、カーネル版数は以下のとりです。
なお、MariaDB、PHPともに、インストールしてから設定変更等は一切していません。

```console:CentOS版数の確認
[root@server ~]# cat /etc/redhat-release
CentOS Linux release 8.3.2011
```

```console:カーネル版数の確認
[root@server ~]# uname -r
4.18.0-240.el8.x86_64
```

```console:PHP版数
[root@server ~]# php -v
PHP 7.2.24 (cli) (built: Oct 22 2019 08:28:36) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies
```

```console:MariaDB版数
[root@server ~]# mariadb -V
mariadb  Ver 15.1 Distrib 10.5.8-MariaDB, for Linux (x86_64) using readline 5.1
```

#3 事前準備
straceコマンドで、シンボル名を表示するため、各種debuginfoパッケージをインストールしました。
なお、dnfの使い方は、[dnfコマンドの使い方](https://qiita.com/hana_shin/items/25f8f4167666cb7fa5b9)を参照してください。

```console:debuginfoパッケージのインストール
[root@server ~]# dnf --enablerepo=debuginfo -y install kernel-debuginfo-4.18.0-240.el8.x86_64
[root@server ~]# dnf --enablerepo="debuginfo" -y install glibc-debuginfo-2.28-127.el8.x86_64
[root@server ~]# dnf --enablerepo=debuginfo -y install MariaDB-server-debuginfo-10.5.8
```


#4 MariaDBの状態について
##4.1 スタックトレースの確認
pstreeコマンドでMariaDBのプロセス構成を確認しました。
MariaDBは、プロセスと複数のスレッドから構成されているようです。
なお、MariaDBの使い方は、[MariaDBの使い方](https://qiita.com/hana_shin/items/4d7e9f07e206e989834b)を参照してください。

```console:MariaDBのプロセス構成
[root@server ~]# pstree -Ap|less
-snip-
           |-mariadbd(1894)-+-{mariadbd}(1895)
           |                |-{mariadbd}(1896)
           |                |-{mariadbd}(1897)
           |                |-{mariadbd}(1898)
           |                |-{mariadbd}(1907)
           |                |-{mariadbd}(1908)
           |                |-{mariadbd}(2040)
           |                |-{mariadbd}(2049)
           |                `-{mariadbd}(2092)
```

次に、psコマンドでプロセスの状態を確認しました。PIDは全て同じですが、LWPが異なります。
つまり、★印のプロセスが親プロセス、それ以外は親プロセスが生成したスレッドになります。
さらに、`wchan`を参照すると次のことがわかります。
・親プロセス：`x64_sys_poll`カーネル関数を呼び出してスリープしている。
・スレッド：`-`となっていることから、常にスリープしているのではないことがわかります。
また、実効ユーザ、実効グループともに、`mysql`であることがわかります。

なお、psコマンドは、[psコマンドの使い方](https://qiita.com/hana_shin/items/a1c5400458f8ca04a809)を参照してください。

```console:psコマンドによるプロセスの確認
[root@server ~]# ps -LC mariadbd -o comm,ppid,pid,lwp,user,euser,group,egroup,wchan
COMMAND            PPID     PID     LWP USER     EUSER    GROUP    EGROUP   WCHAN
mariadbd ★           1    1894    1894 mysql    mysql    mysql    mysql    x64_sys_poll
mariadbd              1    1894    1895 mysql    mysql    mysql    mysql    -
mariadbd              1    1894    1896 mysql    mysql    mysql    mysql    -
mariadbd              1    1894    1897 mysql    mysql    mysql    mysql    -
mariadbd              1    1894    1898 mysql    mysql    mysql    mysql    -
mariadbd              1    1894    1907 mysql    mysql    mysql    mysql    -
mariadbd              1    1894    1908 mysql    mysql    mysql    mysql    -
mariadbd              1    1894    2040 mysql    mysql    mysql    mysql    -
mariadbd              1    1894    2049 mysql    mysql    mysql    mysql    -
mariadbd              1    1894    2092 mysql    mysql    mysql    mysql    -
```

次に、ユーザ空間のスタックトレースを確認しました。
親プロセス(LWP=1894)のスタックトレースを以下に抜粋しました。
親プロセスは、pollシステムコールを呼び出していることがわかります。
スタックの確認方法は、[ハングアップしたプロセスのスタックを確認する方法](https://qiita.com/hana_shin/items/10c7e42f0d327ccf4328)を参照してください。

```console:ユーザ空間のスタックトレース
[root@server ~]# gstack 1894
-snip-
Thread 1 (Thread 0x7f44e2987980 (LWP 1894)):
#0  0x00007f44e03bfca1 in __GI___poll (fds=fds@entry=0x7ffe780a7e60, nfds=nfds@entry=2, timeout=timeout@entry=-1) at ../sysdeps/unix/sysv/linux/poll.c:29
#1  0x00007f44e03db872 in __poll_chk (fds=fds@entry=0x7ffe780a7e60, nfds=nfds@entry=2, timeout=timeout@entry=-1, fdslen=fdslen@entry=24) at poll_chk.c:27
#2  0x00005576c033653f in poll (__timeout=-1, __nfds=2, __fds=0x7ffe780a7e60) at /usr/include/bits/poll2.h:41
#3  handle_connections_sockets () at /usr/src/debug/MariaDB-10.5.8/src_0/sql/mysqld.cc:6219
#4  0x00005576c0337628 in mysqld_main (argc=<optimized out>, argv=<optimized out>) at /usr/src/debug/MariaDB-10.5.8/src_0/sql/mysqld.cc:5668
#5  0x00007f44e02f17b3 in __libc_start_main (main=0x5576c02f1c50 <main(int, char**)>, argc=1, argv=0x7ffe780a82a8, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7ffe780a8298) at ../csu/libc-start.c:308
#6  0x00005576c032b87e in _start () at /usr/src/debug/MariaDB-10.5.8/src_0/sql/sql_basic_types.h:153
```

次に、カーネル空間のスタックトレースを確認してみます。
ユーザ空間で呼び出したpollシステムコールの延長でpoll_schedule_timeout
カーネル関数を呼び出していることがわかります。

```console:カーネル空間のスタックトレース
[root@server ~]# cat /proc/1894/stack
[<0>] poll_schedule_timeout.constprop.13+0x42/0x70
[<0>] do_sys_poll+0x493/0x520
[<0>] do_restart_poll+0x46/0x80
[<0>] do_syscall_64+0x5b/0x1a0
[<0>] entry_SYSCALL_64_after_hwframe+0x65/0xca
```

ちなみに、poll_schedule_timeout関数は、以下のようになっています。
243行目の延長で呼び出すschedule関数でプロセスの切り替えをしています。
つまり、他プロセスにCPU実行権を渡し、自身はスリープします。

```console:poll_schedule_timeout関数
[root@server ~]# less -N /usr/src/debug/kernel-4.18.0-240.el8/linux-4.18.0-240.el8.x86_64/fs/select.c
-snip-
    236 static int poll_schedule_timeout(struct poll_wqueues *pwq, int state,
    237                           ktime_t *expires, unsigned long slack)
    238 {
    239         int rc = -EINTR;
    240
    241         set_current_state(state);
    242         if (!pwq->triggered)
    243                 rc = schedule_hrtimeout_range(expires, slack, HRTIMER_MODE_ABS);
    244         __set_current_state(TASK_RUNNING);
    245
    246         /*
    247          * Prepare for the next iteration.
    248          *
    249          * The following smp_store_mb() serves two purposes.  First, it's
    250          * the counterpart rmb of the wmb in pollwake() such that data
    251          * written before wake up is always visible after wake up.
    252          * Second, the full barrier guarantees that triggered clearing
    253          * doesn't pass event check of the next iteration.  Note that
    254          * this problem doesn't exist for the first iteration as
    255          * add_wait_queue() has full barrier semantics.
    256          */
    257         smp_store_mb(pwq->triggered, 0);
    258
    259         return rc;
    260 }
```


##4.2 Listenしているポート番号の確認
mariadbdがListenしているポート番号を確認してみます。
IPv4ソケットでは、Listenしていないことがわかります。
なお、lsofの使い方は、[lsofコマンドの使い方](https://qiita.com/hana_shin/items/cdc58fe8f4f7705c7a8a)を参照してくだい。

```console:待ち受けポート番号の確認
[root@server ~]# lsof -c mariadbd -a -i4 -P
```

mariadbdはPv6ソケットでListenしていることがわかります。
`*:3306`となっているので、任意のインタフェースにおいて、
TCPの3306番ポートでListenしていることがわかります。

```console:待ち受けポート番号の確認
[root@server ~]# lsof -c mariadbd -a -i6 -P
COMMAND   PID  USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
mariadbd 1894 mysql   20u  IPv6  45715      0t0  TCP *:3306 (LISTEN)
```

なお`/etc/services`を確認すると、以下のように3306が予約されていることがわかります。

```console:servicesファイルの確認
[root@server ~]# cat /etc/services |grep -w 3306
mysql           3306/tcp                        # MySQL
mysql           3306/udp                        # MySQL
```

次に、ncコマンドを使って、mariadbdにアクセスしてみます。
このとき、別ターミナルを起動して、tsharkを実行しておきます。
tsharkの使い方は、[tsharkコマンドの使い方](https://qiita.com/hana_shin/items/0d997d9d9dd435727edf)を参照してくだい。

```console:tsharkの起動
[root@server ~]# tshark -i lo -nn -Y 'tcp.port==3306'
Running as user "root" and group "root". This could be dangerous.
Capturing on 'Loopback'
```

ncコマンド(IPv4オプション指定)で、mariadbdにアクセスしてみます。
mariadbdがパスワードの入力を求めていることがわかります。
ncコマンドは、[ncコマンドの使い方](https://qiita.com/hana_shin/items/97e6c03ac5e5ed67ce38)を参照してくだい。

```console:IPv4でアクセスした場合
[root@server ~]# nc -4 localhost 3306
X
5.5.5-10.5.8-MariaDB7_Z%HxH< 415Y>l&t7U)gmysql_native_password
```
このときの、tsharkの実行結果は、以下のようになります。

```console:IPv4でアクセスした場合
[root@server ~]# tshark -i lo -nn -Y 'tcp.port==3306'
Running as user "root" and group "root". This could be dangerous.
Capturing on 'Loopback'
    1 0.000000000    127.0.0.1 → 127.0.0.1    TCP 74 60760 → 3306 [SYN] Seq=0 Win=43690 Len=0 MSS=65495 SACK_PERM=1 TSval=592463577 TSecr=0 WS=128
    2 0.000030745    127.0.0.1 → 127.0.0.1    TCP 74 3306 → 60760 [SYN, ACK] Seq=0 Ack=1 Win=43690 Len=0 MSS=65495 SACK_PERM=1 TSval=592463577 TSecr=592463577 WS=128
    3 0.000049633    127.0.0.1 → 127.0.0.1    TCP 66 60760 → 3306 [ACK] Seq=1 Ack=1 Win=43776 Len=0 TSval=592463577 TSecr=592463577
    4 0.000466691    127.0.0.1 → 127.0.0.1    MySQL 158 Server Greeting proto=10 version=5.5.5-10.5.8-MariaDB
    5 0.000516772    127.0.0.1 → 127.0.0.1    TCP 66 60760 → 3306 [ACK] Seq=1 Ack=93 Win=43776 Len=0 TSval=592463578 TSecr=592463578
    6 10.011803163    127.0.0.1 → 127.0.0.1    TCP 66 3306 → 60760 [RST, ACK] Seq=93 Ack=1 Win=43776 Len=0 TSval=592473589 TSecr=592463578
```

上記パケットキャプチャを図示すると、以下のようになります。
`()`内はIPv4アドレスとポート番号を表しています。

TPCコネクションを確立したあと、mariadbdからバージョン情報を含むTCPパケットが
ncコマンドに送信されています。
そのあと、ncコマンドはRSTパケットを送信して、TCPコネクションを切断しています。
TCPの状態は、[TCPの各種状態の作り方](https://qiita.com/hana_shin/items/f9bdfaebdfd078291a5a)を参照してください。

```cosnole:シーケンス図
  nc                                                                   mariadbd
(127.0.0.1:60760)                                                 (127.0.0.1:3306)
  |                                                                        |
  |--------------------------------- SYN --------------------------------->|
  |<-------------------------------- SYN+ACK ------------------------------|
  |--------------------------------- ACK --------------------------------->|
  |                                                                        |
  |<--- MySQL 158 Server Greeting proto=10 version=5.5.5-10.5.8-MariaDB ---|
  |--------------------------------- ACK --------------------------------->|
  |                                                                        |
  |--------------------------------- RST --------------------------------->|
  |                                                                        |
```

次に、IPv6パケットでmariadbdにアクセスしてみます。
IPv4のときとシーケンスはかわりません。
IPパケットのIPv4アドレスがIPv6アドレスにかわっただけです。
・IPv4：`127.0.0.1`
・IPv6：`::1`

```console:IPv6でアクセスした場合
[root@server ~]# nc -6 localhost 3306
X
5.5.5-10.5.8-MariaDB`geGp#Vj 3\UvqU7QrlEUmysql_native_passwordNcat: Connection reset by peer.
```


```cosnole:シーケンス図
[root@server ~]# tshark -i lo -nn -Y 'tcp.port==3306'
Running as user "root" and group "root". This could be dangerous.
Capturing on 'Loopback'
    1 0.000000000          ::1 → ::1          TCP 94 50636 → 3306 [SYN] Seq=0 Win=43690 Len=0 MSS=65476 SACK_PERM=1 TSval=4254461623 TSecr=0 WS=128
    2 0.000052804          ::1 → ::1          TCP 94 3306 → 50636 [SYN, ACK] Seq=0 Ack=1 Win=43690 Len=0 MSS=65476 SACK_PERM=1 TSval=4254461623 TSecr=4254461623 WS=128
    3 0.000074953          ::1 → ::1          TCP 86 50636 → 3306 [ACK] Seq=1 Ack=1 Win=43776 Len=0 TSval=4254461623 TSecr=4254461623
    4 0.000391710          ::1 → ::1          MySQL 178 Server Greeting proto=10 version=5.5.5-10.5.8-MariaDB
    5 0.000438039          ::1 → ::1          TCP 86 50636 → 3306 [ACK] Seq=1 Ack=93 Win=43776 Len=0 TSval=4254461624 TSecr=4254461624
```


#5 PHPからMariaDBへのアクセス
次に、PHPソースコードからデータベースに接続してみます。
MariaDBのほうは、`testdb`データベースをあらかじめ作成しておきます。
MariaDBの使い方は、[MariaDBの使い方](https://qiita.com/hana_shin/items/4d7e9f07e206e989834b)を参照ください。

```console:ソースコード
[root@server ~]# cat tp.php
#!/usr/bin/php
<?php
  $pdo = new PDO('mysql:host=127.0.0.1;dbname=testdb', 'root', 'admin');
  $pdo = null;
?>
```

PHPスクリプト実行時のシステムコールの呼び出しをstraceで確認してみました。
straceの使い方は、[straceコマンドの使い方](https://qiita.com/hana_shin/items/a03150552e2f70994248)を参照ください。

```console:straceの実行結果
[root@server ~]# strace -s 4096 -e trace=network ./tp.php
socket(AF_INET6, SOCK_DGRAM, IPPROTO_IP) = 3
socket(AF_INET6, SOCK_DGRAM, IPPROTO_IP) = 3
socket(AF_INET, SOCK_STREAM, IPPROTO_IP) = 3
connect(3, {sa_family=AF_INET, sin_port=htons(3306), sin_addr=inet_addr("127.0.0.1")}, 16) = -1 EINPROGRESS (現在処理中の操作です)
getsockopt(3, SOL_SOCKET, SO_ERROR, [0], [4]) = 0
setsockopt(3, SOL_TCP, TCP_NODELAY, [1], 4) = 0
setsockopt(3, SOL_SOCKET, SO_KEEPALIVE, [1], 4) = 0
recvfrom(3, "X\0\0\0", 4, MSG_DONTWAIT, NULL, NULL) = 4
recvfrom(3, "\n5.5.5-10.5.8-MariaDB\0\20\0\0\0$3re'.O'\0\376\367\10\2\0\377\201\25\0\0\0\0\0\0\17\0\0\0s/.;ysT.$g]|\0mysql_native_password\0", 92, MSG_DONTWAIT, NULL, NULL) = 88
sendto(3, "\204\0\0\1\215\242\33\0\0\0\0\300\10\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0root\0\0241\256\234\276R\33\t\16\367\360$\7\210\256`elA\33\361testdb\0mysql_native_password\0,\f_client_name\7mysqlnd\f_server_host\t127.0.0.1", 136, MSG_DONTWAIT, NULL, 0) = 136
recvfrom(3, "\7\0\0\2", 4, MSG_DONTWAIT, NULL, NULL) = 4
recvfrom(3, "\0\0\0\2@\0\0", 92, MSG_DONTWAIT, NULL, NULL) = 7
sendto(3, "\1\0\0\0\1", 5, MSG_DONTWAIT, NULL, 0) = 5

+++ exited with 0 +++
```

|システムコール | 実施内容 |
|:----------|:-------|
|socket     |3回実行しています。最終的にIPv4,TCPで使用するソケットが作成されています。戻り値はFD(File Descriptor)を表しますので、これ以降のシステムコールは、FD=3を指定して実行しています。  |
|connect    |TCP 3way handshakeを開始します。宛先IP`127.0.0.1`、宛先ポート番号`3306`に対してTCP SYNパケットを送信しています。|
|getsockopt |ソケットで発生したエラーの有無を確認します。戻り値が0なので、エラーは発生していません。   |
|setsockopt |以下のソケットオプションを設定しています。<br> TCP_NODELAY：Nagleアルゴリズムの無効化<br>SO_KEEPALIVE：一定時間パケットのやりとりがないTCPコネクションのクローズ|
|sendto     |第4パラメータ(flags)にMSG_DONTWAITを指定しています。つまり、送信データをカーネル送信バッファにためることなく、即送信するようにカーネルに指示をしています。 |
|recvfrom   |第4パラメータ(flags)にMSG_DONTWAITを指定しています。つまり、受信データがカーネル受信バッファに届くまでプロセスがスリープすることはありません（非同期I/O)。   受信データが受信バッファに存在しない場合、recvfromが`-1`で復帰します。|


#6 プロセスとスレッドの関係
親プロセスに対してstraceを実行します。

```console:親プロセスに対してstraceを実行
[root@server ~]# strace -ffT -p 1894 -e trace=network
strace: Process 1894 attached with 9 threads
```

PHPスクリプトを実行します。

```console:PHPスクリプト実行
[root@server ~]# ./tp.php
```

親プロセス(pid=1894)が実行する`accept4`がFD=39を返していることがわかります。
そして、スレッド`(*)`がFD=39を使って、`sendto`,`recvfrom`を実行していることがわかります。
`(*)`以下の実行結果ではpid 2092。pidという表記よりLWPとした方がよいと思う。

```console:strace実行結果
[root@server ~]# strace -ffT -p 1894 -e trace=network
strace: Process 1894 attached with 9 threads
[pid  1894] accept4(20, {sa_family=AF_INET6, sin6_port=htons(33530), sin6_flowinfo=htonl(0), inet_pton(AF_INET6, "::ffff:127.0.0.1", &sin6_addr), sin6_scope_id=0}, [128->28], SOCK_CLOEXEC) = 39 <0.000430>
strace: Process 2092 attached
[pid  1894] accept4(20, 0x7ffe780a7f00, [128], SOCK_CLOEXEC) = -1 EAGAIN (リソースが一時的に利用できません) <0.000405>
[pid  1894] accept4(20, 0x7ffe780a7f00, [128], SOCK_CLOEXEC) = -1 EAGAIN (リソースが一時的に利用できません) <0.000052>
[pid  2092] setsockopt(39, SOL_IP, IP_TOS, [8], 4 <unfinished ...>
[pid  1894] accept4(20,  <unfinished ...>
[pid  2092] <... setsockopt resumed>)   = 0 <0.000057>
[pid  1894] <... accept4 resumed>0x7ffe780a7f00, [128], SOCK_CLOEXEC) = -1 EAGAIN (リソースが一時的に利用できません) <0.000058>
[pid  2092] setsockopt(39, SOL_TCP, TCP_NODELAY, [1], 4 <unfinished ...>
[pid  1894] accept4(20,  <unfinished ...>
[pid  2092] <... setsockopt resumed>)   = 0 <0.000039>
[pid  1894] <... accept4 resumed>0x7ffe780a7f00, [128], SOCK_CLOEXEC) = -1 EAGAIN (リソースが一時的に利用できません) <0.000041>
[pid  1894] accept4(20, 0x7ffe780a7f00, [128], SOCK_CLOEXEC) = -1 EAGAIN (リソースが一時的に利用できません) <0.000039>
[pid  2092] getpeername(39,  <unfinished ...>
[pid  1894] accept4(20,  <unfinished ...>
[pid  2092] <... getpeername resumed>{sa_family=AF_INET6, sin6_port=htons(33530), sin6_flowinfo=htonl(0), inet_pton(AF_INET6, "::ffff:127.0.0.1", &sin6_addr), sin6_scope_id=0}, [128->28]) = 0 <0.000058>
[pid  1894] <... accept4 resumed>0x7ffe780a7f00, [128], SOCK_CLOEXEC) = -1 EAGAIN (リソースが一時的に利用できません) <0.000084>
[pid  2092] setsockopt(39, SOL_SOCKET, SO_KEEPALIVE, [1], 4 <unfinished ...>
[pid  1894] accept4(20,  <unfinished ...>
[pid  2092] <... setsockopt resumed>)   = 0 <0.000063>
[pid  1894] <... accept4 resumed>0x7ffe780a7f00, [128], SOCK_CLOEXEC) = -1 EAGAIN (リソースが一時的に利用できません) <0.000109>
[pid  1894] accept4(20, 0x7ffe780a7f00, [128], SOCK_CLOEXEC) = -1 EAGAIN (リソースが一時的に利用できません) <0.000043>
[pid  2092] sendto(39, "X\0\0\0\n5.5.5-10.5.8-MariaDB\0\f\0\0\0N$"..., 92, MSG_DONTWAIT, NULL, 0 <unfinished ...>
[pid  1894] accept4(20,  <unfinished ...>
[pid  2092] <... sendto resumed>)       = 92 <0.000232>
[pid  1894] <... accept4 resumed>0x7ffe780a7f00, [128], SOCK_CLOEXEC) = -1 EAGAIN (リソースが一時的に利用できません) <0.000238>
[pid  2092] recvfrom(39, 0x7f44980209e8, 4, MSG_DONTWAIT, NULL, NULL) = -1 EAGAIN (リソースが一時的に利用できません) <0.000025>
[pid  2092] recvfrom(39, "\204\0\0\1", 4, MSG_DONTWAIT, NULL, NULL) = 4 <0.000036>
[pid  2092] recvfrom(39, "\215\242\33\0\0\0\0\300\10\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 132, MSG_DONTWAIT, NULL, NULL) = 132 <0.000013>
[pid  2092] sendto(39, "\7\0\0\2\0\0\0\2@\0\0", 11, MSG_DONTWAIT, NULL, 0) = 11 <0.000046>
[pid  2092] recvfrom(39, 0x7f44980209e8, 4, MSG_DONTWAIT, NULL, NULL) = -1 EAGAIN (リソースが一時的に利用できません) <0.000008>
[pid  2092] recvfrom(39, "\1\0\0\0", 4, MSG_DONTWAIT, NULL, NULL) = 4 <0.000021>
[pid  2092] recvfrom(39, "\1", 1, MSG_DONTWAIT, NULL, NULL) = 1 <0.000069>
```
上記より、次のことがわかる・
・親プロセス：TCPコネクションの受付
・スレッド：データベースに対する操作の実行


#7 まとめ
・次の1行は、PHPスクリプトからMariaDBにTCPコネクションを確立後、
ID/PASSを送信していることがわかった。
`$pdo = new PDO('mysql:host=127.0.0.1;dbname=testdb', 'root', 'admin');`
・MariaDBの親プロセス、スレッドの関係
親プロセス：TCPコネクションの受付
スレッド：データベースに対する操作

#Z　参考情報