20190411のMySQLに関する記事は7件です。

[Rails][mysql] 絵文字を保存できるようにする

「?」みたいな絵文字をMySQLに保存したい。

方法

my.cnf に以下を追記

「/etc/my.cnf」「/etc/mysql/my.cnf」あたりにあるはず。

my.cnf
[mysql]
default-character-set=utf8mb4

[mysqld]
character-set-server = utf8mb4
skip-character-set-client-handshake
character-set-server = utf8mb4
collation-server = utf8mb4_general_ci
init-connect = SET NAMES utf8mb4
innodb_file_format = Barracuda
innodb_file_per_table = 1
innodb_large_prefix

database.ymlを変更

database.yml
encoding: utf8 # 変更前

encoding: utf8mb4 # 変更後

databaseを作成

bash
bundle exec rake db:create
bundle exec rake db:migrate

参考

https://qiita.com/okamu_/items/5eb81688849fbe351350
https://y-hilite.com/3192/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Rails][MySQL] 絵文字を保存できるようにする

「?」みたいな絵文字をMySQLに保存したい。

方法

my.cnf に以下を追記

「/etc/my.cnf」「/etc/mysql/my.cnf」あたりにあるはず。

※最後の3行は、MariaDB >= 10.3.1 ならデフォルトで設定されているので追記しなくてよい。
 (設定自体が出来なくなってもいる。 参考

my.cnf
[mysql]
default-character-set=utf8mb4

[mysqld]
character-set-server = utf8mb4
skip-character-set-client-handshake
character-set-server = utf8mb4
collation-server = utf8mb4_general_ci
init-connect = SET NAMES utf8mb4
innodb_file_format = Barracuda         # MariaDB >= 10.3.1 なら削除
innodb_file_per_table = 1              # MariaDB >= 10.3.1 なら削除
innodb_large_prefix                    # MariaDB >= 10.3.1 なら削除

※AWSのRDSでこの設定をする場合はこちら

database.ymlを変更

database.yml
# 変更前
encoding: utf8

# 変更後
encoding: utf8mb4
charset: utf8mb4
collation: utf8mb4_unicode_ci

databaseを作成

bash
bundle exec rake db:create
bundle exec rake db:migrate

確認

bash
mysql # mysqlにログイン
mysql
use データベース名; # データベースを選択
show variables like 'character_set%'; # 文字コード表示

こんな風に表示されるはず。

+--------------------------+--------------------------------+
| Variable_name            | Value                          |
+--------------------------+--------------------------------+
| character_set_client     | utf8mb4                        |
| character_set_connection | utf8mb4                        |
| character_set_database   | utf8mb4                        |
| character_set_filesystem | binary                         |
| character_set_results    | utf8mb4                        |
| character_set_server     | utf8mb4                        |
| character_set_system     | utf8                           |
| character_sets_dir       | /usr/share/mysql-8.0/charsets/ |
+--------------------------+--------------------------------+

追記(経験談)

AWSのproduction環境で上記設定をした時、database.ymlが反映されず、SQL発行時に「SET NAMES utf8」が出てしまい、絵文字が保存出来ないという状況が起きました。インスタンスを再起動してみたら直りました。

Capistranoを使っていて、「shared/config/」にdatabase.ymlを置いていたので、これが理由なのかなと思っています。

参考

https://qiita.com/okamu_/items/5eb81688849fbe351350
https://y-hilite.com/3192/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【MySQL+Java】採番プロシージャ

はじめに

MySQL + Java な環境で採番の仕組みを用意する必要があったので、
下記の記事を参考に作ってみました。

MySQL で採番テーブル
MySQL で シーケンス 機能実現
LAST_INSERT_IDを使って採番テーブルを扱う

OracleのシーケンスがMySQLにもあればなー。

環境

MySQL 5.7
Java 8

やったこと

  • 採番用のテーブルを用意する。×2
  • 採番プロシージャを用意する。×2
  • Javaで検証する。

採番用テーブル

テーブル1
CREATE TABLE `seq1` (
  `prefix` varchar(8) NOT NULL,
  `id` int(11) NOT NULL DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=utf8
テーブル2
CREATE TABLE `seq2` (
  `prefix` varchar(8) NOT NULL,
  `id` int(11) NOT NULL DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=utf8

それぞれ1レコードずつ登録しておく。

採番プロシージャ

プロシージャ1
CREATE DEFINER=`trial`@`localhost` PROCEDURE `nextval_seq1`(out seqnum varchar(12))
BEGIN
    START TRANSACTION;
    update seq1 set id = LAST_INSERT_ID(id + 1);
    select CONCAT(prefix, LPAD(LAST_INSERT_ID(id), 10, '0')) into seqnum from seq1;
    COMMIT;
END
プロシージャ2
CREATE DEFINER=`trial`@`localhost` PROCEDURE `nextval_seq2`(out seqnum varchar(12))
BEGIN
    START TRANSACTION;
    update seq2 set id = LAST_INSERT_ID(id + 1);
    select CONCAT(prefix, LPAD(LAST_INSERT_ID(id), 10, '0')) into seqnum from seq2;
    COMMIT;
END

検証Javaコード

以下の観点で検証コードを実装しました。

  • マルチスレッドでも問題ない事
  • プロシージャを利用してもコネクションが死なない事
  • 複数の採番用テーブル、採番プロシージャでちゃんと?採番される事
    private void execute(String[] args) {
        ExecutorService pool = Executors.newFixedThreadPool(100);
        try {
            List<Callable<Boolean>> taskList = new ArrayList<>();
            for (int i = 0; i < 50; i++) {
                taskList.add(() -> callProcedureAndUpdatePrefix1());
                taskList.add(() -> callProcedureAndUpdatePrefix2());
            }
            pool.invokeAll(taskList);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            pool.shutdown();
        }
    }

    private boolean callProcedureAndUpdatePrefix1() {
        try (Connection conn = ConnectionManager.getConnection()) {
            conn.setAutoCommit(false);
            callProcedure1(conn);
            updateSeq1(conn);
            conn.commit();
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    private boolean callProcedureAndUpdatePrefix2() {
        try (Connection conn = ConnectionManager.getConnection()) {
            conn.setAutoCommit(false);
            callProcedure2(conn);
            updateSeq2(conn);
            conn.commit();
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    private void callProcedure1(Connection con) throws SQLException {
        try (CallableStatement cs = con.prepareCall("call nextval_seq1(?)")) {
            cs.executeQuery();
            System.out.println(cs.getString(1));
        }
    }

    private void callProcedure2(Connection con) throws SQLException {
        try (CallableStatement cs = con.prepareCall("call nextval_seq2(?)")) {
            cs.executeQuery();
            System.out.println(cs.getString(1));
        }
    }

    private void updateSeq1(Connection con) throws SQLException {
        boolean b = (new Random()).nextInt(99) % 2 == 0 ? true : false;
        String sql = "update seq1 set prefix = " + (b ? "'XA'" : "'XB'");
        try (Statement st = con.createStatement()) {
            st.execute(sql);
        }
    }

    private void updateSeq2(Connection con) throws SQLException {
        boolean b = (new Random()).nextInt(99) % 2 == 0 ? true : false;
        String sql = "update seq2 set prefix = " + (b ? "'YA'" : "'YB'");
        try (Statement st = con.createStatement()) {
            st.execute(sql);
        }
    }

検証結果

XA0000000001
YA0000000006
YA0000000004
XA0000000006
XA0000000005
XA0000000002
XA0000000004
XA0000000007
YA0000000003
YA0000000005
XA0000000019
XA0000000018
YA0000000012
XA0000000013
YA0000000008
YA0000000011
YA0000000007
YA0000000015
YA0000000013
XA0000000003
XA0000000012
XA0000000016
XA0000000015
YA0000000014
XA0000000014
XA0000000017
YA0000000009
YA0000000010
YB0000000022
XB0000000023
XB0000000022
YB0000000021
YB0000000019
YA0000000018
YB0000000020
YA0000000016
YB0000000023
XA0000000020
YA0000000017
XA0000000021
XA0000000025
XB0000000026
XA0000000024
XA0000000011
XA0000000008
YA0000000001
XA0000000010
XA0000000009
YB0000000029
YB0000000026
YB0000000028
YB0000000030
YA0000000002
YB0000000024
YB0000000025
YB0000000027
YA0000000034
YA0000000037
YA0000000031
YA0000000032
YA0000000033
YA0000000035
YA0000000038
YA0000000039
YA0000000036
YA0000000043
XA0000000027
XA0000000029
XA0000000035
YA0000000041
XA0000000034
YA0000000042
YA0000000040
XA0000000030
XA0000000028
XA0000000036
XA0000000033
XA0000000032
XA0000000031
XB0000000045
XB0000000046
XB0000000041
XB0000000047
XB0000000048
XB0000000038
YB0000000050
XB0000000037
XB0000000050
YA0000000045
YB0000000047
YA0000000044
XB0000000042
XB0000000043
XB0000000044
XB0000000049
XB0000000039
XB0000000040
YB0000000048
YB0000000046
YB0000000049

以上

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

CakePHP2系でLOAD DATA LOCAL INFILE のエラー

CSVファイルをDBにぶち込むのに LOAD DATA LOCAL INFILEを使いたかった

MySQLクライアントでやってみたら難なくできたのにPHPでやろうとするとこんなエラー

PDOStatement::execute(): LOAD DATA LOCAL INFILE forbidden

使えないの...><

PDOの設定を変えたいので、
/Config/database.phpを書き換えます。

databse.php
public $default = array(
        'datasource' => 'hoge',
        'persistent' => false,
        'host' => 'hoge',
        'port' => '',
        'login' => 'root',
        'password' => 'password',
        'database' => 'dbs',
        'schema' => '',
        'prefix' => '',
        'encoding' => 'utf8',
        //以下を追加
        "flags" => array(
            PDO::MYSQL_ATTR_LOCAL_INFILE => PDO::MYSQL_ATTR_LOCAL_INFILE
        )
    );

でよろし。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

docker-compose から docker network を少しだけ理解したい

docker-compose での疑問

docker公式っぽいやつの docker-compose の例 : https://docs.docker.com/compose/gettingstarted/

ここでのapp.pyredishostが以下のように指定されています。

cache = redis.Redis(host='redis', port=6379)

なぜ host='redis'で別のコンテナのredisにつなぐことが出来るんだい?

上記の公式サイトには以下のように解説されています。

In this example, redis is the hostname of the redis container on the application’s network. We use the default port for Redis, 6379.

はぇ〜

application’s network ってなに

ここでいう application’s network とは docker container におけるネットワークのことっぽい?

docker-compose up 等で起動されたコンテナ群は、すべて同一のネットワークに所属することになるようです。

参考にさせていただいた記事 : https://qiita.com/roba4coding/items/efd3a38db08eb476d412

つまりどういうことだってばよ

docker inspect コマンドを使うことでふわっと概要がわかります。

inspect するためにまず コンテナのIDを調べます。

$ docker ps
CONTAINER ID        IMAGE                        COMMAND                  CREATED             STATUS              PORTS                    NAMES
aa690f2ae5d4        example_docker_compose_web   "python app-server/s…"   2 minutes ago       Up 2 minutes        0.0.0.0:5000->5000/tcp   example_docker_compose_web_1
4f39c7c9cc47        redis:alpine                 "docker-entrypoint.s…"   2 minutes ago       Up 2 minutes        6379/tcp                 example_docker_compose_redis_1

inspect します。

$ docker inspect 4f39c7c9cc47
...
            "Networks": {
                "example_docker_compose_default": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": [
                        "4f39c7c9cc47",
                        "redis"
                    ],
                    ...
                }
            }
...

コンテナのID : 4f39c7c9cc47(example_docker_compose_redis_1) が redis というエイリアスで登録されているのですね。

このエイリアスを使うことが出来るのが、同一 docker network に所属するものだけだということですね。

上記でいうと コンテナ aa690f2ae5d4(example_docker_compose_web_1) が エイリアスを使うことが出来るということですね。

どこで redis というエイリアスの名前が決まったかということですが、docker-compose.ymlに書いてある servicesの直下の定義の名のことみたいですね。

version: '3'
services:
  # ↓これ
  web:
    build: .
    ports:
     - "5000:5000"
  # ↓これ
  redis:
    image: "redis:alpine"

docker network ってなに

docker network ls で現在存在する docker network の一覧を見ることができます。

$ docker network ls
NETWORK ID          NAME                               DRIVER              SCOPE
ac27f0096628        bridge                             bridge              local
e72136cdac25        example_docker_compose_default     bridge              local
d2b0552308a8        host                               host                local

ここで 最初の docker-composeexample で作られたであろう example_docker_compose_defaultdocker networkの詳細を見てみましょう。

コマンドは docker network inspect です。

$ docker network inspect example_docker_compose_default
[
    {
        "Name": "example_docker_compose_default",
        "Id": "e72136cdac25de9dbc9875467aacd59e916ba8b7122cbede91f81387a2a18715",
...
        "Containers": {
            "4f39c7c9cc478dc30aca3bd21c6dc1eda2bb189c0b5264a325f98b38ad8cd7c6": {
                "Name": "example_docker_compose_redis_1",
                ...
            },
            "aa690f2ae5d483c0c2cf213d9929a3d49025859464abe095f861c05bbb66ee32": {
                "Name": "example_docker_compose_web_1",
                ...
            }
        },
...
]

Containers に 先ほど docker-compose で作られたコンテナ達がありますね。コンテナIDも一致しているようです。

参考にさせていただいた記事 : https://qiita.com/TsutomuNakamura/items/ed046ee21caca4a2ffd9

docker-compose を使わずにやってみよう

今回は別の例として mysql が起動しているコンテナ に対して python が起動したコンテナからアクセスしたいと思います。

docker networkを知らなかった時

まずmysqlのコンテナをたてます。

$ docker run -e MYSQL_ROOT_PASSWORD=root -d -p 3307:3306 --name test-mysql mysql:5.6
4f7475ea1ac4db5d4aad630cbe585faf2dca39db61d7e405e14e9554414bd486

python が起動しているコンテナから mysqlのコンテナにアクセスするためには、そのコンテナのアドレスを知る必要があります。

$ docker exec test-mysql cat /etc/hosts
127.0.0.1   localhost
.. 
172.17.0.2  4f7475ea1ac4

こうすることでようやく接続することができました。(もっといいやり方あったら教えてください..)

>>> import mysql.connector
>>> conn = mysql.connector.connect(host="172.17.0.2",user="root",password="root",port=3306,database="mysql")
>>> conn.is_connected()
True

docker networkを使った場合

まず、コンテナが所属するための docker network を作ります。

docker networkに関して参考にさせていただいたブログ : https://www.sambaiz.net/article/7/

$ docker network create -d bridge test-network
25d355cf117def0e6ce801341e1b535320cd688435a436ce063dc49bbb3c9c0e
$ docker network ls
NETWORK ID          NAME                               DRIVER              SCOPE
ac27f0096628        bridge                             bridge              local
e72136cdac25        example_docker_compose_default     bridge              local
d2b0552308a8        host                               host                local
07263a6d0de1        test-network                       bridge              local

コンテナが作成される時に、作ったネットワークに所属してもらうようにするには、--net オプションを使うようです。

$ docker run -e MYSQL_ROOT_PASSWORD=root -d -p 3307:3306 --name test-mysql --net=test-network --net-alias mysql  mysql:5.6
0f901dbda634c3d05cce10d2bf91fb0ef82d39fbd1b1046f284db522bc8387f8

こうすることで、docker-compose で起動したコンテナと同様にエイリアスが登録されます。

$ docker inspect 0f901dbda634
...
            "Networks": {
                "test-network": {
                    ...
                    "Aliases": [
                        "mysql",
                        "0f901dbda634"
                    ]
            ...
            }
...

そして python が起動するコンテナも同一docker network上にたててあげれば、

$ docker run -it --net=test-network test-python
>>> import mysql.connector
>>> conn = mysql.connector.connect(host='mysql',user='root',password='root',port=3306,database='mysql')
>>> conn.is_connected()
True

無事、コンテナを名前解決できるようになり、host='mysql' で接続できているのがわかると思います。

--link の場合

$ docker run -e MYSQL_ROOT_PASSWORD=root -d -p 3307:3306 --name test-mysql --net=test-network  mysql:5.6
2d96784fb9ae77cec276558d3439e3b455782bb4bac4291b76b65a27d5c27480
$ docker run -it --net=test-network --link test-mysql:mysql test-python
Python 3.7.3 (default, Mar 27 2019, 23:48:15)
[GCC 8.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import mysql.connector
>>> conn = mysql.connector.connect(host='mysql',user='root',password='root',port=3306,database='mysql')
>>> conn.is_connected()
True

同様に繋ぐことが出来ました。

link の場合 AliasesではなくLinksに名前解決するための情報が書かれていました。

...
            "Networks": {
                "test-network": {
                    ...
                    "Links": [
                        "test-mysql:mysql"
                    ],
                    "Aliases": [
                        "d0d2ed057334"
                    ],
                ...
                }
            }
...

--net-alias--link の使い分けに関してはまだ知識が浅いのでいつか調べたいと思います。

まとめ

docker-compose を使おう!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

コマンドプロンプト自体の文字コードの変更-MySQLで文字化けした時の対処-

コマンドプロンプトで日本語が文字化けしてしまう!

コマンドプロンプト上でMySQLの各カラムを表示すると、日本語のみ文字化けしてしまっていた。

MariaDB [testdb]> insert into test(name,skill)values('みき','HTML');

MariaDB [testdb]> select* from test;
+----+------+-------+
| id | name | skill |
+----+------+-------+
|  1 | ・ン・ォ | HTML  |
+----+------+-------+

これを解決するのに、丸3日くらいかかりました。
途中嫌になって放り出していましたが(笑)、teratailで神様のような方々からご回答頂き、解決することができました。
原因はいつも通り単純だったわけですが…

前提・環境

・プログラミング学習を始めて4か月くらいの新人プログラマー
・今までMySQLは自分のMacで触っていたが今回は会社のWindowsを使っており、コマンドでの文字化けは初体験。
・Windows10
・XAMPP 7.2.6
・MySQL Ver 15.1 Distrib 10.1.33-MariaDB, for Win32
※SQL文はコマンドプロンプトで打ち込んでいます

結論

コマンドプロンプト自体の文字コードと、DB・テーブルの文字コードが一致していなかったことが原因。
chcpでコマンドプロンプト自体の文字コードを確認し、chcp 65001で文字コードをUTF8に変更したらちゃんと表示されました。

試したこと

せっかく3日もかけたので、試したこと全部載せておきます。
初心者さんの参考になれば。

①まずはDBの文字コード設定を確認。

MariaDB [testdb]> show variables like "chara%";
+--------------------------+--------------------------------+
| Variable_name            | Value                          |
+--------------------------+--------------------------------+
| character_set_client     | cp932                          |
| character_set_connection | cp932                          |
| character_set_database   | utf8                           |
| character_set_filesystem | binary                         |
| character_set_results    | cp932                          |
| character_set_server     | utf8                           |
| character_set_system     | utf8                           |
| character_sets_dir       | C:\xampp\mysql\share\charsets\ |
+--------------------------+--------------------------------+
MariaDB [testdb]> show create database testdb;
+----------+-----------------------------------------------------------------+
| Database | Create Database                                                 |
+----------+-----------------------------------------------------------------+
| testdb   | CREATE DATABASE `testdb` /*!40100 DEFAULT CHARACTER SET utf8 */ |
+----------+-----------------------------------------------------------------+

デフォルトがutf8なのが分かります。

②my.iniの[client]と[mysqld]の文字コードをutf8に書き換え
参考URL:https://qiita.com/YusukeHigaki/items/2cab311d2a559a543e3a

この記事を参考にmy.iniの2箇所を書き換えましたが、DBの設定を確認すると文字コードの設定が最初と変わっていなかったので

③my.iniの[mysql]のdefault-set-characterもutf8に書き換え
で、DBの設定を確認

MariaDB [(none)]> show variables like "chara%";
+--------------------------+--------------------------------+
| Variable_name            | Value                          |
+--------------------------+--------------------------------+
| character_set_client     | utf8                           |
| character_set_connection | utf8                           |
| character_set_database   | utf8                           |
| character_set_filesystem | binary                         |
| character_set_results    | utf8                           |
| character_set_server     | utf8                           |
| character_set_system     | utf8                           |
| character_sets_dir       | C:\xampp\mysql\share\charsets\ |
+--------------------------+--------------------------------+

ちゃんとutf8に変わりました。
この状態で新たにDB作成し、INSERT。

MariaDB [(none)]> create database test2;

MariaDB [test2]> create table table1(id int auto_increment,name varchar(20),skill varchar(20),index(id));

MariaDB [test2]> insert into table1(name,skill)values('みき','PHP');

MariaDB [test2]> select* from table1;
+----+------+-------+
| id | name | skill |
+----+------+-------+
|  1 | ?ン・?  | PHP   |
+----+------+-------+

これでもダメだったので、

https://teratail.com/questions/58424を参考にset namesしてみた

MariaDB [(none)]> set names cp932;

MariaDB [(none)]> create database newone;
MariaDB [newone]> create table tbl3(id int,name varchar(10),skill varchar(10));
MariaDB [newone]> insert into tbl3(id,name,skill)values(1,'みき','HTML');

MariaDB [newone]> select* from tbl3;
+------+------+-------+
| id   | name | skill |
+------+------+-------+
|    1 | ・ン・ォ | HTML  |
+------+------+-------+

これでもダメだったので、もうお手上げ状態。
teratailに質問を投げて、返ってきた回答をくまなく試しました。

⑤文字コードを「utfmb4」に設定した

MariaDB [(none)]> show variables like "chara%";
+--------------------------+--------------------------------+
| Variable_name            | Value                          |
+--------------------------+--------------------------------+
| character_set_client     | utf8mb4                        |
| character_set_connection | utf8mb4                        |
| character_set_database   | utf8                           |
| character_set_filesystem | binary                         |
| character_set_results    | utf8mb4                        |
| character_set_server     | utf8                           |
| character_set_system     | utf8                           |
| character_sets_dir       | C:\xampp\mysql\share\charsets\ |
+--------------------------+--------------------------------+

MariaDB [test1]> create table test2(id int,name varchar(20))default charset=utf8mb4;
MariaDB [test1]> insert into test2(id,name)values(1,'みき');
MariaDB [test1]> select* from test2;
+------+------+
| id   | name |
+------+------+
|    1 | ?ン・?  |
+------+------+

⑥文字コードをsjisに設定した
参照:https://teratail.com/questions/153238

MariaDB [test3]> SET character_set_results = sjis;
MariaDB [test3]> SET character_set_client = sjis;
MariaDB [test3]> create table test2(id int,name varchar(20))default charset=sjis;
MariaDB [test3]> insert into test2(id,name)values(1,'みき');
MariaDB [test3]> select* from test2;
+------+------+
| id   | name |
+------+------+
|    1 | ???  |
+------+------+

これでついに解決!

teratailで神様のような回答を頂き、解決。

sjis自体が文字化けしている事に対して

そこから設定すべきです。Mysqlの問題以前かも知れません。
https://qiita.com/user0/items/a9116acc7bd7b70ecfb0

とコメントいただき、コマンドプロンプト自体の文字コードをUTF8に変更(テーブルの文字コードがUTF8であるため)。

# chcp
現在のコード ページ: 932
# chcp 65001

Active code page: 65001

で、確認してみると…

MariaDB [trainingdb]> select* from userprofile;
+--------+-------------+-----------+-----------------+--------------+
| userid | name        | bloodtype | origin          | skill        |
+--------+-------------+-----------+-----------------+--------------+
|      6 | みき         | O       | 九州・沖縄      | HTML,CSS     |
|      7 | miki         | O       | 九州・沖縄      | HTML,CSS,PHP |
|      8 | みき         | A       | 神奈川県        | HTML,CSS     |
+--------+--------------+-----------+-----------------+---------------+

苦節3日、やっとのことで解決しました!!
teratailで優しいご回答をくださった皆さんには感謝しかないです。

こういう初歩的な問題でハマるの早くやめたい・・・!
初学者の皆さん、一緒に頑張っていきましょう。。。

teratailでの質問

https://teratail.com/questions/183601

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Auroraの性能検証(EC2との比較,データ量と計算時間の関係)

TL;DR

下記の観点で,AWS Auroraの性能検証を行いました.

  • AWS Auroraと,EC2上に構成したMariaDBで性能比較しました
    • RDBベンチマークTPC-Hによる計算時間の計測
      • BI(Business Intelligence)向けの処理で,比較的大きな複数のテーブルからなるDBに対してクエリを発行するものです.
      • 意思決定(Decision Making)などで使用される傾向がある処理です
    • どちらかというと,Webサービスで使用される際の性能ではなく,バッチ処理での性能評価をしています
  • データ量と処理時間の関係

    • 対象データ量ごとにAWS Auroraでの性能評価を行います
  • この記事は,下記の記事の続編です.この記事から読み始めることもできます

実験設定

TPC-Hは,使用するテーブルサイズを指定する(SF: Scale Factor)ことができるので,
SF=1, 4, 8でベンチマークするテーブルを用意しました.

Aurora

以下の表のパラメータでAuroraを設定します.

設定名 パラメータ
エディション MySQL 5.6 との互換性
DBエンジン Aurora(MySQL)-5.6.10a
インスタンスタイプ db.r5.large 2vCPU, 16GiB RAM
マルチAZ なし

MariaDB

  • t2.smallを作成
  • MariaDBインストール,自動起動の設定
sudo yum -y update
sudo yum -y install mariadb mariadb-server
sudo systemctl start mariadb
sudo systemctl enable mariadb
  • 初期設定(ルートパスワードなどの設定)
sudo mysql_secure_installation

その他は,auroraと同じように,テーブルの作成,データの登録,インデックスの作成を行います.

実験手順

  • Query1からQuery22まで,順番に実行し,
    • クエリの実行ごとに RESET QUERY CACHE; を実行し,キャッシュクリアしてパフォーマンスを正しい計測を行います
for i in `seq 1 22`; do
(time mysql -u [ユーザ名] -h [DBMSエンドポイント] -D [データベース名] -p[パスワード] < ${i}.sql) >> log.txt 2>&1;
mysql -u [ユーザ名] -h [DBMSエンドポイント] -p[パスワード] -e 'RESET QUERY CACHE;'
done

計測結果

TPC-Hで使用するテーブルおよびクエリの詳細は,ドキュメント(tpc-h_v2.18.0.pdf)を参照してください.

AuroraとEC2の比較

計算結果

  • AWS auroraと,EC2(t2.small)で,SF=1 (1GiB)のデータを対象にしたベンチマーク結果を示します
    • sf1.png

考察:フルマネージドRDBサービスauroraの利点

  • インスタンスタイプに応じた性能が得られているように見えます
  • auroraはec2に比べて計算時間上の利点が示されています
  • インスタンスタイプの差以上に,EC2よりも良い性能を示すクエリもあります(Query9, 10, 13, 14, 18)
    • 大きなテーブル(lineitem)の結合処理を含むクエリ(Query 9, 10, 14, 18)
    • likeを含むクエリ(Query 13, 14)
    • これらの,重たい処理を含む場合に,auroraの利点が得られるようです

Auroraの結果の比較

計算結果

  • AWS auroraで, SF=1, 4, 8 を対象にした計算結果を示します

aurora.png

考察: RDBのスケーラビリティ問題

  • データ量に応じて,計算時間を要します
    • 結合処理を含む場合(Query1以外)は,データの量に対して, $O(n^2)$ の計算時間を要します
      • MySQLの結合処理はNested-loop joinであることから,苦手な処理です
    • 大きなテーブル(lineitem)の結合を含むクエリで,極端に計算時間が大きくなるものもあります(Query3, 8, 9, 10, 12, 14, 16, 18, 20, 21)
      • メモリ不足で計算時間が増大したようにも見えます

まとめ

  • Auroraは,フルマネージドなRDBとして,一定程度のBI(Bussiness Intelligence)処理に効果が得られることが示されました
    • EC2上に自分でDBを作るよりも,性能が出そうです
  • とは言え,テーブルサイズが大きくなると,(EC2はもちろんauroraでも)計算時間が激増してしまいます
    • そもそもRDBで実施するクエリではないとも言えるので,巨大データの結合を含むような処理は,別の方法を考えたほうが良いかもしれません

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む