MySQL | さゆフィクション http://it.kensan.net/it aws wordpress などなどゆるーく書いてます Sun, 30 Jun 2024 01:05:05 +0000 ja hourly 1 https://wordpress.org/?v=6.5.2 https://it.kensan.net/wp-content/uploads/2023/03/cropped-icon-32x32.png MySQL | さゆフィクション http://it.kensan.net/it 32 32 【SQL】SELECT文でスロークエリを発生させる方法〜SELECT SLEEPを使ってスロークエリーを発生させる〜 https://it.kensan.net/select-slow-query.html Sat, 01 Jun 2024 01:57:13 +0000 http://52.192.132.163/it/?p=1990 スロークエリを意図的に発生させる方法について記載します。

なぜスロークエリを発生させるのか

スロークエリをわざと発生させることで、以下のようなことに役立ちます。

  • パフォーマンステスト: システムがスロークエリに対してどのように反応するかを評価
  • 監視システムのテスト: スロークエリが発生した場合、監視ツールが適切にアラートを出すか確認
  • トラブルシューティング: スロークエリが原因でシステムがどのように挙動するかを再現し、問題解決

SELECT SLEEP(5); とは?

「SELECT SLEEP(5);」はSQLコマンドで、指定した時間(この場合は5秒)だけ待機します。このコマンドを利用することで、意図的にスロークエリを発生させることができます。

実際に使ってみる

以下はMySQLでの使用例です。


mysql> SELECT SLEEP(5);
+----------+
| SLEEP(5) |
+----------+
|        0 |
+----------+
1 row in set (5.00 sec)

このように、クエリが5秒間実行されます。

スロークエリログに記録されるか確認する

次に、このクエリがスロークエリログに記録されるかを確認します。MySQLでは、スロークエリの閾値を設定することができます。例えば、2秒以上のクエリをスロークエリとして記録するには、以下のように設定します。

SET GLOBAL long_query_time = 2;

この設定を行った後に「SELECT SLEEP(5);」を実行すると、このクエリはスロークエリログに記録されます。

まとめ

「SELECT SLEEP(5);」を使用してスロークエリを発生させる方法について記載しました。このSQLを使用することで、パフォーマンステストや監視システムのチェックが容易になり、トラブルシューティングにも役立つかと思います!

]]>
EC2のAmazonLinux2023にMySQLサーバをインストールする https://it.kensan.net/amazonlinux2023-mysql-install.html Sat, 11 May 2024 04:27:25 +0000 http://18.182.2.245/it/?p=1875 AmazonLinux2023の内部にMySQLをインストールする方法についてです。
EC2をOS:AmazonLinux2023で立ち上げ、そこにMySQLをインストールします。

EC2立ち上げ

EC2を立ち上げます。

  • インスタンスタイプは適切に選択
  • AMIでAmazonLinux2023を選択

EC2に接続

以下のいずれかの方法で接続します。

  • EC2 Instance Connect
  • セッションマネージャ
  • SSHクライアント

接続方法が分からない場合は、以下の記事をご参照ください。

EC2への4つの接続方法について(Instance Connect/セッションマネージャー/SSH/シリアルコンソール)
「EC2に入りたいけど入れない」 「EC2に入る方法がいくつかあるけど、どれで入るのが良いの?どんな違いがあるの?」という方向けの記事です。 EC2の接続方法は「EC2 Instance Connect」「セッションマネージャー」「SSHクライアント」「EC2 シリアルコンソール」 の4つの接続方法があります。

MySQLインストール

以下のコマンドでインストールします。

sudo dnf -y localinstall https://dev.mysql.com/get/mysql80-community-release-el9-1.noarch.rpm

sudo rpm --import https://repo.mysql.com/RPM-GPG-KEY-mysql-2023

→これを実行しないと「Error: GPG check FAILED」エラーになる

sudo dnf install -y mysql-community-server mysql-community-client mysql-community-devel

以下のコマンドでMySQLを起動します。

sudo systemctl enable mysqld.service

sudo systemctl start mysqld.service

MySQLに接続するための初期パスワードを確認します

sudo less /var/log/mysqld.log

MySQLに接続して、パスワード更新します。

mysql -uroot -p
# パスワード更新
ALTER USER 'root'@'localhost' IDENTIFIED BY '新しいパスワード';

まとめ

以上で完成です。MySQLサーバはRDSを使うケースが多いと思いますが、EC2上にMySQLサーバを立ち上げる場合は上記コマンドで簡単にできます!

]]>
MySQLでユニークインデックスを貼る際のDuplicate entry fuga for key hoge エラー https://it.kensan.net/mysql-duplicate-error.html Sat, 11 May 2024 04:04:04 +0000 http://18.182.2.245/it/?p=1863 MySQLでユニークインデックスを貼る際のDuplicate entry fuga for key hoge エラーについてです。

まず、ユニークインデックスを貼る際の構文については以下の通りです。

alter table {テーブル名} add unique({インデックスを貼るカラム});

インデックスを貼るカラムはカンマ「,」区切りで複数指定できます。

ユニークインデックスを貼る際のDuplicateエラーがどんな場合に発生するか記載していきます。

MySQL8系とMySQL5.7系で若干挙動が異なります。具体的には以下の挙動となります。

Duplicateエラーの発生条件

MySQL8系とMySQL5.7系ともに複数レコードに同じ値があるカラムにはユニークインデックスは貼れない(Duplicateエラーになる)
MySQL8系とMySQL5.7系ともに複数レコードにNULLがあるカラムにはユニークインデックスは貼れる
MySQL5.7系の場合、末尾の半角スペースのみの違いしかない値が複数レコードあるカラムにはユニークインデックスは貼れない(Duplicateエラーになる)(MySQL8系では貼れる)

検証時のコマンドと結果を記載していきます。

MySQL8系

まずバージョン確認

select version();

+-----------+
| version() |
+-----------+
| 8.0.37    |
+-----------+

複数レコードに同じ値があるカラム場合

# テーブル作成
create table user (id int, name varchar(10));

# データ登録
insert into user values (1, 'test_user'),(2, 'test_user');

# ユニークインデックスを貼る
alter table user add unique(name);

# 結果:エラー
ERROR 1062 (23000): Duplicate entry 'test_user' for key 'user.name'

# テーブル削除
drop table user;

複数レコードにNULLがあるカラム場合

# テーブル作成
create table user (id int, name varchar(10));

# データ登録
insert into user values (1, null),(2, null);

# ユニークインデックスを貼る
alter table user add unique(name);

# 結果:成功
Query OK, 0 rows affected (0.02 sec)

# テーブル削除
drop table user;

末尾にスペースがある場合

# テーブル作成
create table user (id int, name varchar(10));

# データ登録
insert into user values (1, 'test_user'),(2, 'test_user ');

# ユニークインデックスを貼る
alter table user add unique(name);

# 結果:成功
Query OK, 0 rows affected (0.02 sec)

# テーブル削除
drop table user;

途中にスペースがある場合

# テーブル作成
create table user (id int, name varchar(10));

# データ登録
insert into user values (1, 'testuser'),(1, 'test user');

# ユニークインデックスを貼る
alter table user add unique(id,name);

# 結果:成功
Query OK, 0 rows affected (0.02 sec)

# テーブル削除
drop table user;

先頭にスペースがある場合

# テーブル作成
create table user (id int, name varchar(10));

# データ登録
insert into user values (1, 'test_user'),(1, ' test_user');

# ユニークインデックスを貼る
alter table user add unique(id,name);

# 結果:成功
Query OK, 0 rows affected (0.01 sec)

# テーブル削除
drop table user;

MySQL5.7系

まずバージョン確認

select version();

+-----------+
| version() |
+-----------+
| 5.7.44    |
+-----------+

複数レコードに同じ値があるカラム場合

# テーブル作成
create table user (id int, name varchar(10));

# データ登録
insert into user values (1, 'test_user'),(2, 'test_user');

# ユニークインデックスを貼る
alter table user add unique(name);

# 結果:エラー
ERROR 1062 (23000): Duplicate entry 'test_user' for key 'name'

# テーブル削除
drop table user;

複数レコードにNULLがあるカラム場合

# テーブル作成
create table user (id int, name varchar(10));

# データ登録
insert into user values (1, null),(2, null);

# ユニークインデックスを貼る
alter table user add unique(name);

# 結果:成功
Query OK, 0 rows affected (0.02 sec)

# テーブル削除
drop table user;

末尾にスペースがある場合

# テーブル作成
create table user (id int, name varchar(10));
# データ登録
insert into user values (1, 'test_user'),(2, 'test_user ');

# ユニークインデックスを貼る
alter table user add unique(name);

# 結果:エラー
ERROR 1062 (23000): Duplicate entry 'test_user' for key 'name'

# テーブル削除
drop table user;

途中にスペースがある場合

# テーブル作成
create table user (id int, name varchar(10));

# データ登録
insert into user values (1, 'testuser'),(1, 'test user');

# ユニークインデックスを貼る
alter table user add unique(id,name);

# 結果:成功
Query OK, 0 rows affected (0.02 sec)

# テーブル削除
drop table user;

先頭にスペースがある場合

# テーブル作成
create table user (id int, name varchar(10));

# データ登録
insert into user values (1, 'test_user'),(1, ' test_user');

# ユニークインデックスを貼る
alter table user add unique(id,name);

# 結果:成功
Query OK, 0 rows affected (0.01 sec)

# テーブル削除
drop table user;

まとめ

概ね想定通りでしたが、「MySQL5.7系の場合、末尾の半角スペースのみの違いしかない値が複数レコードあるカラムにはユニークインデックスは貼れない」のは意外でした!!

Duplicateエラーの発生条件まとめ

MySQL8系とMySQL5.7系ともに複数レコードに同じ値があるカラムにはユニークインデックスは貼れない
MySQL8系とMySQL5.7系ともに複数レコードにNULLがあるカラムにはユニークインデックスは貼れる
MySQL5.7系の場合、末尾の半角スペースのみの違いしかない値が複数レコードあるカラムにはユニークインデックスは貼れない(MySQL8系では貼れる)

]]>
DockerでLaravel 11のローカル開発環境を構築する。ついでにphpmyadminも入れてみる。 https://it.kensan.net/docker-laravel-11-phpmyadmin.html Mon, 06 May 2024 02:07:18 +0000 http://13.112.45.21/it/?p=1844 DockerでLaravel 11のローカル開発環境を構築していきます。

DB確認用に、phpmyadminもインストールします。

コンテナは以下のような構成です。本番環境に応用できるように、あえてSailは使いません。

  • アプリコンテナ
    • PHP:8.3をインストールして、Laravel 11をインストールする
  • Nginxコンテナ
    • 最新のNginxをインストール
  • DBコンテナ
    • MySQL:8.0.36をインストール
  • phpmyadminコンテナ
    • 最新のphpmyadminをインストール(現時点の最新バージョン5.2.1をインストール)

では実際にローカル開発環境を構築していきます。

各コマンドをコピペで環境構築できます!

Laravelのローカル開発環境構築

まずは、完成系のディレクトリ構成から記載します。

ディレクトリ構成

以下のような構成となります。


├── docker
│   ├── db  # MySQL(DB)コンテナの設定ファイル等を入れるディレクトリ
│   │   └── my.cnf
│   ├── nginx  # nginxコンテナの設定ファイル等を入れるディレクトリ
│   │   └── default.conf
│   └── php  # アプリ(PHP)コンテナの設定ファイル等を入れるディレクトリ
│       └── Dockerfile
└── docker-compose.yml

ディレクトリの作成

上記ディレクトリ構成を作成するためのディレクトリを作成します。

mkdir test_laravel
cd test_laravel/

準備はできましたので、ディレクトリ構成に記載のディレクトリとファイルを作成していきます。

docker-compose.ymlの作成

docker-compose.ymlを作成します。

vi docker-compose.yml

<ファイルの中身>


version: '3'
services:
  app:
    container_name: app_laravel  
    build: ./docker/php
    volumes:
      - .:/var/www
  nginx:
    image: nginx 
    container_name: nginx
    ports:
      - 8000:80 
    volumes:
      - .:/var/www
      - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
    working_dir: /var/www
    depends_on:
      - app
  db:
    image: mysql:8.0.36
    container_name: db
    environment: 
      MYSQL_ROOT_PASSWORD: password
      MYSQL_DATABASE: database 
    command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
    volumes:  
      - ./docker/db/data:/var/lib/mysql
      - ./docker/db/my.cnf:/etc/mysql/conf.d/my.cnf 
      - ./docker/db/sql:/docker-entrypoint-initdb.d
    ports:
      - 3306:3306
  # phpMyAdmin
  phpmyadmin:
    container_name: test_phpmyadmin
    image: phpmyadmin
    environment:
    - PMA_USER=root
    - PMA_PASSWORD=password
    ports:
    - 8080:80

MySQLのmy.confを作成

MySQLのmy.confを作成します。

mkdir docker

mkdir docker/db

vi docker/db/my.conf

<ファイルの中身>


[mysqld]
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci
default_authentication_plugin=mysql_native_password

[client]
default-character-set=utf8mb4

nginxのdefault.confを作成

nginxのdefault.confを作成します。

mkdir docker/nginx

vi docker/nginx/default.conf

<ファイルの中身>


server {
  listen 80;
  root /var/www/laravel-project/public;
  index index.php;
  location / {
    try_files $uri $uri/ /index.php?$query_string;
  }
  location ~ \.php$ {
    try_files $uri =404;
    fastcgi_split_path_info ^(.+\.php)(/.+)$;
    fastcgi_pass app:9000; 
    fastcgi_index index.php;
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_param PATH_INFO $fastcgi_path_info;
  }
}

アプリコンテナのDockerfile作成

アプリコンテナのDockerfile作成を作成します。

mkdir docker/php

vi docker/php/Dockerfile

<ファイルの中身>


FROM php:8.3-fpm

RUN apt-get update \
  && apt-get install -y zlib1g-dev mariadb-client vim libzip-dev \
  && docker-php-ext-install zip pdo_mysql

#Composer install
RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
RUN php composer-setup.php
RUN php -r "unlink('composer-setup.php');"
RUN mv composer.phar /usr/local/bin/composer

ENV COMPOSER_ALLOW_SUPERUSER 1

ENV COMPOSER_HOME /composer

ENV PATH $PATH:/composer/vendor/bin


WORKDIR /var/www

RUN composer global require "laravel/installer"

必要なファイル作成が終わりましたので、コンテナ立ち上げてLaravel11をインストールしていきます。

コンテナ立ち上げ

以下のコマンドでコンテナを立ち上げます。

docker compose up -d

Laravel 11のインストール

アプリコンテナの中に入りLaravel11をインストールします。

# アプリコンテナに入るコマンド
docker compose exec app bash
# Laravel11インストールコマンド
composer create-project --prefer-dist laravel/laravel laravel-project "11.*"

DBの接続先をsqlliteからmysqlに変更します。

vi laravel-project/.env

<ファイルの中身(DB設定箇所を編集します)>

DB_CONNECTION=mysql
DB_HOST=db
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=root
DB_PASSWORD=password

以下のコマンドでマイグレーションを実行します。

cd laravel-project/
php artisan migrate

以上で環境が出来上がりましたので、最後に動作確認します!

動作確認

  • http://localhost:8000/にアクセスして
    • 以下のLaravelの画面が見れればOK
  • http://localhost:8080/にアクセスして
    • phpmyadminが見れればOK

sailを使う場合

以下のコマンドでプロジェクトインストールします。

curl -s "https://laravel.build/example-app" | bash

以下のコマンドでコンテナを立ち上げます。

cd プロジェクト名 && ./vendor/bin/sail up

ブラウザで「http://localhost/」 へアクセスすると以下のエラーが出ます。

SQLSTATE[42S02]: Base table or view not found: 1146 Table 'laravel.sessions' doesn't exist

「RUN MIGRATIONS」をクリックした後、「Refresh now」をクリックするとLaravelのスタート画面が表示されます。成功〜〜

]]>
Amazon Linux2023 に MySQL8をインストール時に、GPG check FAILEDになった際の解決方法 https://it.kensan.net/amazon-linux2023-mysql8-gpg-check-failed.html Sun, 05 May 2024 04:54:58 +0000 http://43.207.2.219/it/?p=1837 Amazon Linux2023 に MySQL8をインストールしようとしたら、エラー(GPG check FAILED)になり、かなりハマりましたので、解決方法を記載します。

以下を実行後すれば、MySQL8をインストールできるようになります。

sudo rpm --import https://repo.mysql.com/RPM-GPG-KEY-mysql-2023
]]>
MySQL5.7系から8系にアップデートする際に、checkForServerUpgradeで注意点を簡単にチェックする https://it.kensan.net/mysql5-7-8-checkforserverupgrade.html Sat, 04 May 2024 07:32:24 +0000 http://54.238.225.16/it/?p=1822 MySQL5.7のサポート期限は以下のようになっています。

Auroraでも2024年10月でサポート期限切れです!

MySQLバージョン MySQL AWS RDSのMySQL AWS AuroraのMySQL
5.7 2023年10月 2024 年 2 月 2024 年 10 月 31 日

(Aurora バージョン2)

8.0 2026年4月30日 未定 未定

(Aurora バージョン3)

Amazon RDS での MySQL のバージョン - Amazon Relational Database Service
Amazon RDS for MySQL のバージョンについて学びます。
Amazon Aurora バージョン - Amazon Aurora
Amazon Aurora のバージョンについて説明します。バージョンには、それぞれのバージョン番号、リリースサイクル、バージョン廃止のタイムラインなどがあります。

 

有償の延長サポートはあるようですが、MySQL8系へアップデートを検討する必要がある状態かと思います。

Amazon Aurora バージョン - Amazon Aurora
Amazon Aurora のバージョンについて説明します。バージョンには、それぞれのバージョン番号、リリースサイクル、バージョン廃止のタイムラインなどがあります。

 

checkForServerUpgradeコマンドを使うと簡単に、MySQL5.7系から8系にアップデートする際の互換性エラー・アップグレードの問題を確認できます。

fw_error_www
fw_error_www

checkForServerUpgradeコマンドについて

機能追加・廃止・予約語の追加などのMySQL8での変更により、MySQL5.7と非互換となっている箇所がないかチェックするツールです。簡単にチェックできるので、是非やってみましょ

ただし、checkForServerUpgradeで全ての問題を拾えるわけではないので、リリースノートの調査をして影響のチェックは必須です。

リリースノートのチェック漏れがないかのチェックざっくり影響内容を見たい場合に有効なコマンドかと思います。

checkForServerUpgradeを使ってみる

まず必要なものをインストールします!

MySQL shellのインストール

MySQL shellをインストールする必要があるので、以下のコマンドでインストールします。

sudo rpm --import https://repo.mysql.com/RPM-GPG-KEY-mysql-2023

→これを実行しないと「Error: GPG check FAILED」エラーになる

sudo yum install mysql-shell

これで準備完了なので、checkForServerUpgradeを実行してみます!

checkForServerUpgradeコマンドの実行

以下のようにcheckForServerUpgradeコマンドを実行できます。

# MySQLに接続する
mysqlsh -uroot

#checkForServerUpgradeコマンド実行!!!
util.checkForServerUpgrade()

まとめ

MySQL5.7系から8系にアップデートする際に使える便利なcheckForServerUpgradeコマンドについて記載しました。よかったらお試しくださーい

]]>
MySQLのデータ更新(UPDATE)・削除(DELETE)を手動実行する際に、安全に実行する方法 https://it.kensan.net/mysql-update-delete-safe.html Sat, 04 May 2024 06:07:07 +0000 http://54.238.225.16/it/?p=1818 つい先日、MySQLのデータ更新(UPDATE)する際に、where句の指定に誤りがあり、想定外のデータが更新されるというトラブルがあり….安全にデータ更新する方法を考えてみました。

ということで、MySQLのデータ更新(UPDATE)・削除(DELETE)を手動実行する際に、安全に実行する方法について書いていきます。

SQL文の記載を誤ると想定外のデータを更新・削除しちゃう怖い作業ですが、これをできるだけ安全にやる方法について記載していきます!

結論から書くとトランザクションを使って安全にデータ更新・削除していきます。

(トランザクションを使うのは基本ですが、、、改めて重要性に気づいたので記載していきます)

 

トランザクションの使い方

    • トランザクションを始めるコマンド
begin;
  • ロールバックするコマンド(誤ったデータ更新をした際に、元に戻すコマンド)
rollback;
  • コミットするコマンド(正しくデータ更新をできた際に、実際にDBに反映するコマンド)
commit;

では次に、データ準備して、実際にロールバックとコミットをしていきます

データ準備

  • テーブル作成
    • 以下のコマンドで、idとnameを持つ、userテーブルを作成
create table user (id int, name varchar(10));
  • データ作成
    • 以下のコマンドで2レコード作成します
insert into user values (1, 'test_user1'),(2, 'test_user2');

トランザクションを使って安全にデータ更新(UPDATE)してみる

rollbackする場合


mysql> begin;
Query OK, 0 rows affected (0.00 sec)
### トランザクションをスタート
mysql> update user set name = 'test1';
Query OK, 2 rows affected (0.00 sec)
Rows matched: 2  Changed: 2  Warnings: 0
### 更新件数を確認して、妥当性を確認

mysql> rollback;
Query OK, 0 rows affected (0.01 sec)
### ロールバックする

mysql> select name from user ;
+------------+
| name       |
+------------+
| test_user1 |
| test_user2 |
+------------+
2 rows in set (0.00 sec)
###ロールバックできたことを確認する

commitする場合


mysql> begin;
Query OK, 0 rows affected (0.00 sec)
### トランザクションをスタート

mysql> update user set name = 'test1' where id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
### 更新件数を確認して、妥当性を確認

mysql> commit;
Query OK, 0 rows affected (0.00 sec)
### コミットする

mysql> select name from user ;
+------------+
| name       |
+------------+
| test1      |
| test_user2 |
+------------+
2 rows in set (0.00 sec)
###コミットできたことを確認する

 

トランザクションを使って安全にデータ削除(DELETE)してみる

rollbackする場合


mysql> begin;
Query OK, 0 rows affected (0.00 sec)
### トランザクションをスタート
mysql> delete from user;
Query OK, 2 rows affected (0.00 sec)
### 更新件数を確認して、妥当性を確認

mysql> rollback;
Query OK, 0 rows affected (0.01 sec)
### ロールバックする

mysql> select name from user ;
+------------+
| name       |
+------------+
| test_user1 |
| test_user2 |
+------------+
2 rows in set (0.00 sec)
###ロールバックできたことを確認する

commitする場合


mysql> begin;
Query OK, 0 rows affected (0.00 sec)
### トランザクションをスタート

mysql> delete from user where id = 1;
Query OK, 1 row affected (0.00 sec)
### 更新件数を確認して、妥当性を確認

mysql> commit;
Query OK, 0 rows affected (0.00 sec)
### コミットする

mysql> select name from user ;
+------------+
| name       |
+------------+
| test_user2 |
+------------+
1 row in set (0.00 sec)
###コミットできたことを確認する

まとめ

MySQLのデータ更新(UPDATE)・削除(DELETE)を手動実行する際は、トランザクションを使って安全にデータ修正していきましょー

]]>
MySQLのInnoDBとMyISAMのパフォーマンス比較をしてみました https://it.kensan.net/mysql-innodb-myisam-performance.html Fri, 18 Aug 2023 20:05:08 +0000 http://3.113.9.194/it/?p=1710 MySQLのInnoDBとMyISAMのパフォーマンス比較をしてみましたー

対象のMySQLのバージョンは5.7と8.0です。

結論としては、

  • INSERTはMyISAMの方が早い
  • SELECT・UPDATE・DELETEは
    • 8.0ではInnoDBの方が早い
    • 5.7ではSELECT・DELETEはMyISAMの方が早い。UPDATEはInnoDBの方が早い

という結果でしたー!

なお、MySQLのバージョンごとのサポート期限は以下のようになっています。

MySQLバージョン MySQL AWS RDSのMySQL AWS AuroraのMySQL
5.7 2023年10月 2024 年 2 月 2024 年 10 月 31 日

(Aurora バージョン2)

8.0 2026年4月30日 未定 未定

(Aurora バージョン3)

検証条件

  • MySQL5.7-MyISAMで、1万件のデータ登録をした後、SELECT・UPDATE・DELETE
  • MySQL5.7-InnoDBで、1万件のデータ登録をした後、SELECT・UPDATE・DELETE
  • MySQL8.0-MyISAMで、1万件のデータ登録をした後、SELECT・UPDATE・DELETE
  • MySQL8.0-InnoDBで、1万件のデータ登録をした後、SELECT・UPDATE・DELETE

比較結果詳細

MySQL5.7 MySQL8.0
MyISAM InnoDB MyISAM InnoDB
1万件データ作成

24.96 sec

53.24 sec

21.39 sec

48.92 sec

1万件カウント

0.00016000 sec

0.00359175 sec

0.00018900 sec

0.00850275 sec

1SELECT

0.00127125 sec

0.00401250 sec

0.07204400 sec

0.00575250 sec

1UPDATE

0.06670350 sec

0.01640200 sec

0.02672875 sec

0.01512700 sec

1DELETE

0.00380375 sec

0.00935400 sec

0.02560125 sec

0.01409000 sec

準備

検証環境:AWS RDS
インスタンスクラス:db.t3.micro


-- 区切り文字を「//」に変更する
DELIMITER //

-- make_sample_dataというテストデータ作成用プロシージャーを作成する

create procedure make_sample_data(in i int)
begin
  declare count int default 0;
  -- 繰り返し
  while count < i do
    set count = count + 1;
    INSERT INTO user VALUES(count, MOD(count,2),CONCAT('usr_name_',count));
  end while;
end
//
-- 区切り文字を「;」に戻す
DELIMITER ;

MySQL5.7 MyISAM

create table user(
id INT,
type INT,
name VARCHAR(255)
) engine MyISAM;

 

mysql> select version();
+------------+
| version()  |
+------------+
| 5.7.41-log |
+------------+
1 row in set (0.00 sec)

◾️1万件データ作成

call make_sample_data(10000);
Query OK, 1 row affected (24.96 sec)

◾️1万件カウント

select count(*) from user;
+----------+
| count(*) |
+----------+
|    10000 |
+----------+
0.00016000 

◾️1件SELECT

select * from user where id = 5000;
+------+------+---------------+
| id   | type | name          |
+------+------+---------------+
| 5000 |    0 | usr_name_5000 |
+------+------+---------------+
0.00127125 

◾️1件UPDATE

update user set name = 'name_5001' where id = 5001;
0.06670350 

◾️1件DELETE

delete from user where id = 5002;
0.00380375 

MySQL5.7 InnoDB

create table user(
id INT,
type INT,
name VARCHAR(255)
) engine InnoDB;

◾️1万件データ作成

call make_sample_data(10000);
Query OK, 1 row affected (53.24 sec)

◾️1万件カウント

select count(*) from user;
+----------+
| count(*) |
+----------+
|    10000 |
+----------+
0.00359175 

◾️1件SELECT

select * from user where id = 5000;
+------+------+---------------+
| id   | type | name          |
+------+------+---------------+
| 5000 |    0 | usr_name_5000 |
+------+------+---------------+
0.00401250

◾️1件UPDATE

update user set name = 'name_500001' where id = 5001;
0.01640200 

◾️1件DELETE

delete from user where id = 5002;
0.00935400 

MySQL8.0 MyISAM

create table user(
id INT,
type INT,
name VARCHAR(255)
) engine MyISAM;
select version();
+-----------+
| version() |
+-----------+
| 8.0.32    |
+-----------+

◾️1万件データ作成

call make_sample_data(10000);
Query OK, 1 row affected (21.39 sec)

◾️1万件カウント

select count(*) from user;
+----------+
| count(*) |
+----------+
|    10000 |
+----------+
0.00018900 

◾️1件SELECT

select * from user where id = 5000;

+------+------+---------------+
| id   | type | name          |
+------+------+---------------+
| 5000 |    0 | usr_name_5000 |
+------+------+---------------+
0.07204400 

◾️1件UPDATE

update user set name = 'name_500001' where id = 5001;
0.02672875 

◾️1件DELETE

delete from user where id = 5002;
0.02560125

MySQL8.0 InnoDB

create table user(
id INT,
type INT,
name VARCHAR(255)
) engine InnoDB;

◾️1万件データ作成

call make_sample_data(10000);
Query OK, 1 row affected (48.92 sec)

◾️1万件カウント

select count(*) from user;
+----------+
| count(*) |
+----------+
|    10000 |
+----------+
0.00850275

◾️1件SELECT

select * from user where id = 5000;
+------+------+---------------+
| id   | type | name          |
+------+------+---------------+
| 5000 |    0 | usr_name_5000 |
+------+------+---------------+
0.00575250 

◾️1件UPDATE

update user set name = 'name_500001' where id = 5001;
0.01512700

◾️1件DELETE

delete from user where id = 5002;
0.01409000 

まとめ

MySQLのInnoDBとMyISAMのパフォーマンス比較をしてみましたー

結論としては、

  • INSERTはMyISAMの方が早い
  • SELECT・UPDATE・DELETEは
    • 8.0ではInnoDBの方が早い
    • 5.7ではSELECT・DELETEはMyISAMの方が早い。UPDATEはInnoDBの方が早い

という結果でしたー!

MyISAMとInnoDBの違いを比較した記事もありますので、よろしければ、ご参照くださいー

MySQL8.0でのMyISAMとInnoDBの違いを比較
MySQL8.0でのストレージエンジンのMyISAMとInnoDBの違いについて記載しています。 InnoDBには、トランザクション機能や外部キー機能がありますので、 MyISAMとInnoDBで迷ったら、多くの主要な機能が備わっているInnoDBを選択しましょ!
]]>
AWS Aurora Serverless v2の用途や注意点 https://it.kensan.net/aws-aurora-serverless-v2.html Sat, 06 May 2023 02:47:37 +0000 http://3.113.9.194/it/?p=1626 Aurora Serverless v2について記載します。

具体的には以下のことについて記載していきます。

  • Aurora Serverless v2とは
  • 用途
  • 注意点

まずはAurora Serverless v2とは?について書いていきます。

Aurora Serverless v2とは

Aurora Serverless v2は、オンデマンドでAuto Scaling設定できるデータベースです。

以下の点について、具体的に記載していきます。

これから記載すること

性能
スケーリング
可用性

性能

ACUという単位で管理されることになります。

1ACUは、約2GB のメモリと、対応する CPU、ネットワークの組み合わせとなります。

ACUは最大、128 ACU を指定することができます。

ACUの最大はどれくらいのインスタンスかというと、メモリは128 ACU × 2GB = 256GBとなります。DBインスタンスクラスタイプがr6gの場合、以下の公式ページから、r6gかつメモリ:256GBのインスタンスを探して、db.r6g.8xlargeが最大ということになります。

Aurora DB インスタンスクラス - Amazon Aurora
DB インスタンスクラスによって、 Amazon Aurora DB インスタンスの計算とメモリの容量を決定します。
ACUの最大の時の性能例

インスタンスクラス:db.r6g.8xlarge
vCPU:32
メモリ(GB):256

 

スケーリング

Aurora Serverless v2は以下に記載されている通り、スケーリングが早く、アクセスのスパイクにも耐えたれそうです。(実際に耐えられるかどうかは、システムがDBへどれくらいの負荷をかけるか次第になるので、要検証ですね)

Amazon Aurora Serverless v2 は、ほんの一瞬で数十万ものトランザクションにスケールできます。スケールに応じて、容量をきめ細かい増分で調整し、アプリケーションが必要とする適切な量のデータベースリソースを提供します。

Amazon Aurora Serverless | AWS
Amazon Aurora Serverless では、管理する DB インスタンスはありません。データベースは、アプリケーションニーズに応じて、容量を自動的に起動、停止、および拡大または縮小します。

また、Aurora Serverless v2 では、以下のようなケースでもスケーリング可能なようです。

  • データベースの接続中
  • SQL トランザクション処理中
  • テーブルロック中
  • 一時テーブルの使用中

可用性

Aurora Serverless v2は、以下の特徴を持ち、高可用性を確立しています。

  • マルチ AZ 対応
  • 自動的フェイルオーバー

次は、用途について記載します。

用途

  • 変化の大きいアクセスやアクセス量が予測できない時
    アクセスが多い時と少ない時の落差が大きい場合や、アクセス量が予測できない場合、データベースは負荷に応じて、自動的にスケーリングしてくれます。
  • 開発時のAuroraの代替
    本番は、Auroraを使用している場合でも、開発環境は、Aurora Serverlessを利用するとコスト削減につながることあります。

次は、注意点です。

 

注意点

以下に注意点を記載します。

  • MySQLの場合、Aurora MySQL バージョン 3のみ対応しています
    • MySQLのバージョン8系しか対応していないということになります
  • ACUは最大、128 ACU(db.r6g.8xlargeくらい)が最大となっています
    • 最大が決まっているため、要注意です
  • Aurora Serverless v1 では Data API が使えましたが、v2ではData APIは使えません。
  • 費用の予測がしにくい
    • 最大ACUから最大の費用は算出することができますが、実際にどれくらいの費用となるかについては予測がしづらいです

まとめ

v1では、SQL実行中はスケーリングできないなどの制限がありましたが、v2では改善され用途が広がりそうですね。素晴らしーい!

]]>
MySQLでSELECT FROMの中でSELECTしてみる〜サブクエリの話〜 https://it.kensan.net/mysql_subquery.html Thu, 04 May 2023 04:03:51 +0000 http://3.113.9.194/it/?p=1624 MySQLでSELECT FROMの中でSELECTしてみるというサブクエリの話です。

具体的には以下のように、SELECTのFROM句の中でのSELECTについて書いていきます。

SELECT {取得したいカラム}
FROM
    (
        SELECT {取得したいカラム}
        FROM {テーブル名}

    ) AS {サブクエリ名}

SELECTのFROM句の中でのSELECTはサブクエリと言います!

SELECTのFROM句以外でもサブクエリは使えますが、今回はSELECTのFROM句に絞ってのサブクエリについて記載します。

試しにサブクエリしてみます!

SELECT FROMの中でSELECTしてみる

まずテーブルを作成します。

create table user(
id INT,
type INT,
name VARCHAR(255)
);

1件INSERTします。

INSERT INTO user VALUES(1, 2,'usr_name_1');

SELECTのFROM句の中でSELECTしてみます。

SELECT
    subquery.id
FROM
    (
        SELECT
            id
        FROM
            user
    ) AS subquery
;

実行結果は以下のようになります。

+------+
| id   |
+------+
|    1 |
+------+
1 row in set (0.00 sec)

SELECT FROMの中でSELECTする際のポイント

ポイント

FROM句の中で(SELECT文)を使用する
(SELECT文) AS でサブクエリ名を付ける
(SELECT文) で取得したカラムについて、外側のSELECT文で取得可能
→サブクエリ名.カラム名で取得できる(上記の例だと「subquery.id」)

上記の例では、「SELECT id FROM user」と実行結果が同じになり、「SELECT FROMの中でSELECTする」意味がない(SQLが複雑になるだけ)です。

次は、実際に「SELECT FROMの中でSELECTする」と効果がある例について記載します。

以降、「SELECT FROMの中でSELECTする」はサブクエリと呼びます。

サブクエリの効果的な使い方

サブクエリを使わないと取得が難しい場合や、サブクエリを使うことで高速化が測れる場合などで使用します。サブクエリ内でデータの絞り込みが行える場合、使用するとクエリの高速化ができます。

サブクエリを使用するケース

サブクエリを使わないと取得が難しい場合
サブクエリを使うことで高速化が測れる場合
→サブクエリ内でデータの絞り込みが行える場合、使用するとクエリの高速化ができるケースがあります。

具体的にクエリ例を記載していきます!

こんなときにサブクエリが使えるよ!という具体例を記載していきます。

まずは、準備です。

準備

◾️テーブル作成

以下のテーブルを作成します。

create table user(
id INT AUTO_INCREMENT,
type INT,
name VARCHAR(255),
PRIMARY KEY (id)
);

create table user_score(
id INT AUTO_INCREMENT,
user_id INT,
score INT,
PRIMARY KEY (id), index(user_id)
);

◾️プロシージャ作成

次は、テストデータはプロシージャーで作成しますのでプロシージャを作成します。

以下の通りデータ作成してくれるようになっています。

  • user.type:カウントを2で割った余りを格納(あまり意味がないカラムです。)
  • user.name:「user_name_{カウント}」の値を格納
  • user.score:ランダムな数値を格納
-- 区切り文字を「//」に変更する
DELIMITER //

-- make_sample_dataというテストデータ作成用プロシージャーを作成する

create procedure make_sample_data(in i int)
begin
  declare count int default 0;
  -- 繰り返し
  while count < i do
    set count = count + 1;
    INSERT INTO user (type, name) VALUES(MOD(count,2),CONCAT('usr_name_',count));
    INSERT INTO user_score (user_id, score) VALUES(count, CEIL(RAND() * 1000));
    INSERT INTO user_score (user_id, score) VALUES(count, CEIL(RAND() * 1000));
    INSERT INTO user_score (user_id, score) VALUES(count, CEIL(RAND() * 1000));
  end while;
end
//
-- 区切り文字を「;」に戻す
DELIMITER ;

◾️100万件インサート

mysql> call make_sample_data(1000000);
Query OK, 1 row affected (4 min 29.74 sec)

◾️バージョン確認

mysql> select version();
+-----------+
| version() |
+-----------+
| 8.0.26    |
+-----------+
1 row in set (0.02 sec)

準備ができたので、サブクエリを使っていきます!

まずは、サブクエリを使わないと取得が難しい場合の例です。

サブクエリを使わないと取得が難しい場合

最も大きいデータや最も小さいデータの取得など、集計した結果の最大値、最小値を求める際には、サブクエリを使うと便利です。

例えば、上記で準備したテーブルのスコアが最も大きいユーザのスコアを取得するなどです。

スコアが最も大きいユーザのスコアを取得するサブクエリSQLは以下となります。

select max(score_sum.sum) as max from (
        SELECT
            `user_id`,
           SUM(`score`) AS `sum`
        FROM
            `user_score`
        GROUP BY
            `user_id`
    ) AS `score_sum`
;
// 実行結果
+------+
| max  |
+------+
| 2998 |
+------+
1 row in set (10.51 sec)

サブクエリを使うことで高速化が測れる場合

サブクエリ内でデータの絞り込みが行える場合、使用するとクエリの高速化ができます。

上記で準備したテーブルで、ユーザのスコアの平均が990のデータを絞り込む例で考えてみます。

◾️サブクエリを取得した場合

SELECT
    `user`.*,
    `score`.`average`
FROM
    (
        SELECT
            `user_id`,
            AVG(`score`) AS `average`
        FROM
            `user_score`
        GROUP BY
            `user_id`
        HAVING `average` > 990
    ) AS `score`
    JOIN
        `user`
    ON  `user`.`id` = `score`.`user_id`
;
// 実行結果
59 rows in set (8.75 sec)

◾️JOINを使用した場合

SELECT
    `user`.*,
    AVG(`user_score`.`score`) AS `average`
FROM
    `user`
    LEFT JOIN
        `user_score`
    ON  `user`.`id` = `user_score`.`user_id`
GROUP BY
    `user_id`
HAVING `average` > 990
;
// 実行結果
59 rows in set (17.68 sec)

サブクエリの方が高速でしたー

まとめ

サブクエリについて記載しましたー

まとめ

SELECTのFROM句の中でのSELECT
→サブクエリと言います!
<サブクエリを使うケース>
サブクエリを使わないと取得が難しい場合
サブクエリを使うことで高速化が測れる場合
→サブクエリ内でデータの絞り込みが行える場合、使用するとクエリの高速化ができるケースがあります。

]]>