- 投稿日:2021-02-28T22:19:59+09:00
MacでMySQL, PostgreSQLをインストールしてセットアップする方法
(1)MySQLについて
MySQLをインストール
brew install mysql
brewが動かない場合はググってインストールしてみてください。
mysql -u root -p
でMySQLのrootのパスワードを設定し、MySQLにログインできる。
データベースの作成
mysql> create database db #データベースの作成
MySQLにログインした状態で上記コマンドを実行する。この場合はdbという名前のデータベースを作成できる。dbを作成したいデータベース名に変更して実行する。
データベースの削除
mysql> drop database db
細かい部分は作成のときと同様(2)PostgreSQLについて
PostgreSQLをインストール
brew install postgresql
PostgreSQLサーバーを起動
postgres -D /usr/local/var/postgres
ユーザーを作成
createuser -P newuser
このnewuserを作りたいユーザー名に変更して使えばよい。データベースを作成
createdb db -O postgres
この場合はdbという名前のデータベースが作成できる。これを好きに変更して実行する。
- 投稿日:2021-02-28T22:13:13+09:00
pt-online-schema-changeを理解する
はじめに
こんにちは、webエンジニアの@an_sonyです。
最近巨大テーブルをalterする機会があり、そのツールとしてpt-online-schema-changeを初めて利用しました。そこで得た知見をまとめてみます。
pt-online-schema-change(以下pt-osc)とは?
主にMySQL向けに作られた運用ツール群perconaの一つです。
テーブルへのread/writeを許容できる状態に保ったままalter操作を実行することを可能にします。なぜpt-oscが必要か
なにも考えずに通常のalter操作を実行すると、共有ロックを取得するのでinsert/updateができなくなります。alterしようとしているテーブルのレコード数が多い場合、その間書き込みプロセスは待ち状態となり処理が完了しないため、サービスが停止してしまいます。
なお、MySQL5.6からはオンラインDDLができるようになりましたが、全てのalter操作ができるわけではないです。例えばインデックスの追加・削除なら可能ですが、カラムのデータ型の変更は不可です。
pt-oscの仕組み
仕組みについては、分かりやすくまとまっている記事がありますので参照ください。
ざっくり流れを記載すると↓
- 旧テーブルと同じスキーマ構造をした、新テーブルを作る
- 新テーブルにalter tableを適用
- トリガーを3つ作成し、旧テーブルのinsert/update/deleteが新テーブルに反映されるようにする
- 旧テーブルのレコードを新テーブルにコピー
- 新旧テーブルを入れ替え(rename)
- 旧テーブルの削除
- 3で作ったトリガーを削除
実行した時のログ↓
Altering `testdb`.`testtable`... Creating new table... Created new table testdb._testtable_new OK. Altering new table... Altered `testdb`.`_testtable_new` OK. 2021-2-27T08:00:32 Creating triggers... 2021-2-27T08:00:32 Created triggers OK. 2021-2-27T08:00:32 Copying approximately 10 rows... 2021-2-27T08:00:32 Copied rows OK. 2021-2-27T08:00:32 Analyzing new table... 2021-2-27T08:00:32 Swapping tables... 2021-2-27T08:00:32 Swapped original and new tables OK. 2021-2-27T08:00:32 Dropping old table... 2021-2-27T08:00:33 Dropped old table `testdb`.`_testtable_old` OK. 2021-2-27T08:00:33 Dropping triggers... 2021-2-27T08:00:33 Dropped triggers OK. Successfully altered `testdb`.`testtable`.注意点
実行中リソースへの負荷が高い
実行中はデータコピーやトリガー処理が走るので、ディスクI/O、CPUに負荷がかかります。
pt-oscにはリソース状況をチェックしながら動作を制御できる便利なオプションが存在しています。
例えば--check-slave-lag
で全スレーブのレプリケーション遅延をチェックできたり、--max-lag
で指定秒数のレプリケーション遅延が発生すると処理を一時停止するといった制御が可能です。十分なディスク容量が必要
一時的なテーブルコピーをするので、相応(具体的な数値は分かりませんでした)のディスク容量が必要です。
デットロックは発生する
pt-oscは旧テーブルのデータを細切れで新テーブルにコピーするツールなので、コピーした瞬間はロックがかかります。その瞬間でクエリがバッティングしてしまうとデットロックが発生します。
テーブルロックを取得するタイミングがある
トリガー作成/削除、またはrename tableのタイミングでテーブルロックを取得します。その間はselectもできなくなるので、同時アクセスが多い場合は処理が詰まりやすいです。
まとめ
便利なツールではありますが、なんでも解決してくれるわけではないので事前に十分な検証が必要です。
できるだけテーブルにアクセスが少ない時間帯を選んだり、実行中はバッチなどによる大量データ処理(read/write)も避けた方が良いです。また、alterしたいテーブルへのread/write頻度が高かったり、デットロックが発生するとサービスへの影響が大きい場合は、他の方法を検討するしかなさそうです。参考
https://www.percona.com/doc/percona-toolkit/LATEST/pt-online-schema-change.html
https://ameblo.jp/principia-ca/entry-12129289966.html
https://dev.mysql.com/doc/refman/5.6/ja/innodb-create-index-overview.html
https://nisshiee.org/blog/2018/105/
- 投稿日:2021-02-28T20:12:46+09:00
【Chart.js × Laravel】Controllerで返されたデータをJavaScriptで操作するには
Controllerで返された値をJavascriptで操作する方法、Javascriptでchart.jsを使って複数のチャートで動的なデータを表示する方法があまり見当たらなかったのでこちらで紹介。
通常Controllerで返された値(配列)は
@foreach
を使ってbladeで表示できる。test.blade.php// as $key => $valueの場合、 @foreach($array_datas as $key => $value) <div class="box"> <p>keyは{{ $key }}で、valueは{{ $value }}だよ</p> </div> @endforeach // as $dataの場合、 @foreach($array_datas as $data) <div class="box"> <p>それぞれのデータは{{ $data }}として表示されたよ。</p> </div> @endforeachbladeでそのままデータを表示する場合は、上記のようにすれば問題ない。
JSライブラリによっては、出力される属性をid
を指定している。
そのため@foreach
との併用の場合少し工夫がいる。chart.jsも同様で、出力される属性は
id
なのでそのまま@foreach
を使うと最初だけchartが表示されて2回目以降は表示されない。これはidがユニークなものでなければならないため、重複したidは2度目以降読み込まれないから。
なので、idで設定しているものをforeach
で回すときはHTML側で{{$key}}
を設定し、それぞれを独立させる必要がある。また、Javascript側でも読み込めるようにforEach
を使って読み込むid
を指定する。まず初めに、chart.jsをそれぞれで読み込めるようにblade側を以下のように変更する。
test.blade.php@foreach($array_datas as $data) <div class="box"> <canvas id="myBarChart{{ $key }}"></canvas> </div> @endforeachkeyを追加することでユニークなidを
id=myBarChart0
、id=myBarChart1
、id=myBarChart2
、のように生成する。javascript側は
document.getElementById()
でユニークなidを読み込むので、それぞれのidを読み込むように、forEachを使って要素ごとの実行を行う。
なお、$foreach
で定義された$array_datas
は@JSON
を使うことでjavascriptでも操作できる。test.blade.php(chart.js以下にscriptを設ける)<script> const array_datas = @JSON($array_datas); //bladeの$array_datasをjavascriptで読み込む const data_keys = Object.keys(array_datas); // それぞれのkeyを取得 data_keys.forEach(el =>{ const chart_id = "myBarChart" + el; // elはdata_keysのそれぞれのkey var ctx = document.getElementById(chart_id); var myBarChart = new Chart(ctx, { // それぞれのidごとにchartが生成される。 }); }); </script>ここまでで
@foreach
の中にあるchartは2回目以降も表示される。
javascript側で常にkey
をみることで、bladeの$keyと連動させることができる。とはいえ、これでは同じチャートの情報を複製しているにすぎない。
ここからはそれぞれのchartでkeyごとに別々のデータを表示する方法を紹介。
chart.jsでは表示されるデータの値を以下のようにdatasets以下のdata配列から参照する。test.blade.phpvar myBarChart = new Chart(ctx, { type: 'bar', data: { labels: ['8月1日', '8月2日', '8月3日', '8月4日', '8月5日', '8月6日', '8月7日'], datasets: [ { label: 'A店 来客数', data: [62, 65, 93, 85, 51, 66, 47], backgroundColor: "rgba(219,39,91,0.5)" },{ label: 'B店 来客数', data: [55, 45, 73, 75, 41, 45, 58], backgroundColor: "rgba(130,201,169,0.5)" },{ label: 'C店 来客数', data: [33, 45, 62, 55, 31, 45, 38], backgroundColor: "rgba(255,183,76,0.5)" } ] }, . . .例えば、Controllerで取得した配列データが連想配列で、そのデータがkey以下に複数の配列が存在する以下のような場合、
それぞれのkey(0,1,2)ごとにデータ("Aサイト" => array3)を参照してchartで表示したい。
こんなときはJavascriptでいったんkeyごとに配列を生成して、それを参照するようにchart.jsに記述する。const array_datas = @JSON($array_datas); const data_keys = Object.keys(array_datas); console.log(data_keys); // ["0", "1", "2"] data_keys.forEach(el =>{ const month = []; const clicks = []; const imps = []; const chart_id = "myBarChart" + el; const array_data = Object.values(array_datas[el]); console.log(array_data); for(var i = 0; i < array_data[0].length; i++){ month.push(array_data[0][i]['month']); clicks.push(array_data[0][i]['click']); imps.push(array_data[0][i]['imps']); } console.log(month); // ["2020-12", "2020-11", "2020-10"] console.log(clicks); // [3, 8, 1] console.log(imps); //[36, 52, 24] var ctx = document.getElementById(chart_id); var myBarChart = new Chart(ctx, { type: 'bar', data: { labels: month, datasets: [ { label: 'クリック数', data: clicks, backgroundColor: "rgba(219,39,91,0.5)", },{ label: '表示回数', data: imps, backgroundColor: "rgba(130,201,169,0.5)" }, ] }, options: { scales: { yAxes: [{ //y軸 ticks: { suggestedMax: 80, suggestedMin: 0, stepSize: 10, } }], xAxes: [{ //X軸 ticks: { font: { size: 3, }, padding: 0 } }] }, layout: { padding: { left: 0, right: 0, top: 0, bottom: 0 } }, plugins: { datalabels: { // 共通の設定はここ font: { size: 14, color: 'rgba(200,60,60,1)', }, anchor: 'end', align: 'end', } }, } }); });bladeも若干調整する。
test.blade.php@foreach($array_datas as $key => $value) <div class="chart-box"> <div>keyの番号{{$key}}番目のチャート情報</div> @foreach ($value as $index_name => $item) <div>~~ {{$index_name}}の結果 ~~</div> @endforeach <canvas id="myBarChart{{ $key }}"></canvas> </div> @endforeach <style> .chart-box { display: inline-block; text-align: center; width: 300px; margin: 30px; background: #f7f7f7; } </style> <script src="{{ asset('js/Chart.bundle.js') }}"></script> <script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@0.7.0"></script>結果、以下のようにそれぞれの値をchartで表示することができる。
おまけ
chart.jsのy軸のラベルはデフォルトでは数値を記述することで,Max,Min,Stepを設定できる。できれば、配列のデータごとにlabelも可変されるとよりみやすくなる。その場合は、下記のように関数を設定して動的にlabelを設定するといい感じのチャートが完成する。
yAxes: [{ //y軸 ticks: { suggestedMax: 80, // ←ここ suggestedMin: 0, stepSize: 10, // ←ここ } }], // ********* 以下のように変更 ********* // yAxes: [{ //y軸 ticks: { suggestedMax: adjastSuggestedMax(month,clicks,imps) suggestedMin: 0, stepSize: adjastSuggestedStep(month,clicks,imps), } }], . . . }); // 以下2つの関数を追加 function adjastSuggestedMax(sc_keyword_month,sc_keyword_clicks,sc_keyword_imps){ max_clicks = Math.max.apply(null, sc_keyword_clicks); max_imps = Math.max.apply(null, sc_keyword_imps); all_max_num = []; all_max_num.push(max_clicks,max_imps); max_num = Math.max.apply(null, all_max_num); if(max_num < 10){ var suggestedMax = 10; } else if(10 <= max_num){ var suggestedMax = max_num + 20; } return suggestedMax; }; function adjastSuggestedStep(sc_keyword_month,sc_keyword_clicks,sc_keyword_imps){ max_clicks = Math.max.apply(null, sc_keyword_clicks); max_imps = Math.max.apply(null, sc_keyword_imps); all_max_num = []; all_max_num.push(max_clicks,max_imps); max_num = Math.max.apply(null, all_max_num); if(max_num < 10){ var suggestedStep = 10; } else if(10 <= max_num){ var suggestedStep = 30; } return suggestedStep; };こうすることで配列の数字の中から最大値を検知してそれに合わせてラベルやステップを表示することができる。if条件をさらに細かくすれば自分好みの可変もできる。
まとめ
今回はbladeのデータをJavascriptで扱う方法と個別チャートの表示方法を紹介しました。
chart.jsに限らず多くのライブラリで同じような方法で個別に生成したりできるので、他のライブラリでも挑戦してみてください。
- 投稿日:2021-02-28T19:52:48+09:00
MySQL group by句を体験してみる
目的
- 自分のgroup by句に対する知識があやふやなので基本的な部分を固めてみる。
- 下記の記事で書いているのに若干まだ理解できていない。これは恥ずかしい。
環境
- ハードウェア環境
項目 情報 OS macOS Catalina(10.15.5) ハードウェア MacBook Pro (13-inch, 2020, Four Thunderbolt 3 ports) プロセッサ 2 GHz クアッドコアIntel Core i5 メモリ 32 GB 3733 MHz LPDDR4 グラフィックス Intel Iris Plus Graphics 1536 MB
- ソフトウェア環境
項目 情報 備考 MySQLバージョン 8.0.19 for osx10.13 on x86_64 Homwbrewを用いてこちらの方法で導入→Mac HomebrewでMySQLをインストールする 情報
- Macに直接MySQLのサーバーを立てて実際にSQLを実行してgroup by句を体験してみることとする。
- 「環境」に記載された条件でgroup by句を体験してみる。
条件
- MySQLにターミナルからログインする事ができること。(パスワードなどがわからなくなってしまった方はこちら→Mac ローカル環境の MySQL 8.x のrootパスワードを忘れた時のリセット方法)
下記SQLを実行してテスト用データベースとテーブルを作成した。
create database group_by_test; use group_by_test; create table users( id int, user_flag int, age int, name varchar(255) ); insert into users value(1, 1, 20, 'user1'); insert into users value(2, 1, 22, 'user2'); insert into users value(3, 2, 18, 'user3'); insert into users value(4, 1, 16, 'user4'); insert into users value(5, 2, 22, 'user5'); insert into users value(6, 1, 10, 'user6');上記のSQLを実行したあとのusersテーブルの状態を下記に記載する。
mysql> select * from users; +------+-----------+------+-------+ | id | user_flag | age | name | +------+-----------+------+-------+ | 1 | 1 | 20 | user1 | | 2 | 1 | 22 | user2 | | 3 | 2 | 18 | user3 | | 4 | 1 | 16 | user4 | | 5 | 2 | 22 | user5 | | 6 | 1 | 10 | user6 | +------+-----------+------+-------+体験
下記を実行してuser_flagが1と2のユーザーの年齢の平均値を別々に出力してみる。
select user_flag, avg(age) as avg_age from users group by user_flag;下記のように出力された。
+-----------+---------+ | user_flag | avg_age | +-----------+---------+ | 1 | 17.0000 | | 2 | 20.0000 | +-----------+---------+group by句は「出力結果を指定したカラムでまとめてくれる」とおぼえておこう。
- 投稿日:2021-02-28T17:03:18+09:00
Windows10 Home に Docker Desktop をインストールしてMySQLサーバを起動してSQLで動作確認するまでの手順2021/02
Windows を最新にする
バージョンが降るとインストールできない場合があるので、最新かどうか確認します。
Windows キーを押して設定ボタン
を押します。
表示された設定画面の右上の Windows Update 部分をクリックします。
Docker ホームページを開く
下記 URL の Docker 公式ページを開きます。(英語ページです)
https://www.docker.com
インストーラーをダウンロード
Docker Desktop の Download for Windows ボタンを押します。
インストーラーを実行
Docker Desktop Installer.exe がダウンロードされますので、
ダウンロードが完了したら実行します。
終了したら Close and restart ボタンを押して再起動します。
カーネル更新プログラムのインストール
再起動後以下の画面が出たら https://aka.ms/wsl2kernel のリンクを開きます。
※この画面は後で使うので消さないでおいておきます。
x64 マシン用 WSL2 Linux カーネル更新プログラムパッケージ のリンクをクリックします。
保存ボタンを押してダウンロードします。
ダウンロードした wsl_update_x64.msi を実行します。
この画面は今回特に使わないので閉じます。
MySQLで動作確認
まずバージョン情報を見てみます。
cmd と入力して OK ボタンを押します。
Docker バージョン情報表示
docker version
と入力し Enter キーを押して実行します。
つぎに、試しにMySQLを動かしてみます。
以下のコマンドを入力して実行します。MySQLサーバのDockerコンテナを起動
MySQLサーバの起動docker run -e MYSQL_ROOT_PASSWORD=password mysql
ずらずらと表示が出てきて ready for connections. という行が表示されれば起動完了です。
docker run
で docker コンテナという仮想サーバ的なものを起動します。
mysql
というのが立ち上げるコンテナの種類になります。
-e MYSQL_ROOT_PASSWORD=password
は環境変数の設定で MySQL の root ユーザのパスワードを指定しています。次にサーバに入ってみます。
Windows+R キーでもう一つ別のコマンドプロンプトを起動します。
以下のコマンドで動作中のコンテナ一覧が確認できます。
Dockerコンテナ一覧表示
実行中Dockerコンテナ一覧を表示docker psこの一覧にある mysql の CONTAINTER ID をコピーしておきます。
※IDは毎回変わります。今回は 11576ddb996a です。そのID を指定して以下のコマンドを実行します。
Dockerコンテナのシェルに入る
Dockerコンテナのシェルに入るdocker exec -it 11576ddb996a bashそうすると
root@11576ddb996a:/#
が表示されます。
これは mysql サーバ内のシェルに入った状態です。次に以下のコマンドでMySQLクライアントを起動します。
MySQLクライアント起動
MySQLクライアント起動mysql -uroot -pパスワードを聞かれるので環境変数で指定した password を指定します。
以下のように、mysql> が表示されればOKです。
MySQLクライアント起動後root@11576ddb996a:/# mysql -uroot -p Enter password: Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 10 Server version: 8.0.23 MySQL Community Server - GPL Copyright (c) 2000, 2021, Oracle and/or its affiliates. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql>データベース一覧
データベース一覧を表示してみます。
データベース一覧表示show databases;データベース一覧表示後mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | mysql | | performance_schema | | sys | +--------------------+ 4 rows in set (0.01 sec)データベース作成
データベースを作成してみます。
データベース作成create database testdb;データベース作成結果mysql> create database testdb; Query OK, 1 row affected (0.04 sec)データベース切り替え
testdbに入ります。
データベース切り替えuse testdb;データベース切り替え結果mysql> use testdb; Database changedテーブル作成
テーブルを作ってみます。
テーブル作成create table test_users(id int auto_increment primary key, name varchar(10), birthday date);テーブル作成結果mysql> create table test_users(id int auto_increment primary key, name varchar(10), birthday date); Query OK, 0 rows affected (0.04 sec)レコード挿入
レコードを挿入してみます。
レコード挿入insert into test_users(name,birthday) values('tanaka', '1990-01-02');レコード挿入結果mysql> insert into test_users(name,birthday) values('tanaka', '1990-01-02'); Query OK, 1 row affected (0.04 sec)テーブル内容確認
テーブルの内容を確認します。
テーブル内容確認select * from test_users;テーブル内容確認結果mysql> select * from test_users; +----+--------+------------+ | id | name | birthday | +----+--------+------------+ | 1 | tanaka | 1990-01-02 | +----+--------+------------+ 1 row in set (0.00 sec)問題なく使えますね。(日本語を使っていないのは実は現状では扱えないためです。この後日本語も扱う方法を紹介します。)
後片付け
mysql>
が表示されている状態で exit を実行し、MySQL クライアントを終了します。
root@11576ddb996a:/#
が表示されている状態で exit を実行し、シェルを終了します。コンテナ停止
次に以下のコマンドでMySQLサーバを停止します。
※ 11576ddb996a は コンテナID です。docker ps で確認できるものです。Dokcerコンテナ停止docker stop 11576ddb996a停止されているか docker ps で確認します。
実行中コンテナ一覧C:\Users\nakaz>docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES一覧が空になっているのでコンテナが停止したことが確認できました。
コンテナの起動
再度起動したいときは以下のコマンドを実行します。
コンテナ起動docker start 11576ddb996a起動したいときにコンテナID がわからなくなった場合は、以下のコマンドで確認できます。
停止中も含めたコンテナ一覧docker ps -a
停止中も含めたコンテナ一覧実行結果C:\Users\nakaz>docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 11576ddb996a mysql "docker-entrypoint.s…" 41 minutes ago Exited (0) 4 minutes ago naughty_curranコンテナ削除
もしコンテナが不要になって消したい場合は以下のコマンドを実行します。
コンテナ削除docker rm 11576ddb996a
コンテナ削除結果C:\Users\nakaz>docker rm 11576ddb996a 11576ddb996aコンテナ削除後確認
再度
docker ps -a
を実行すると消えていることが確認できます。コンテナ削除後確認結果C:\Users\nakaz>docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESMySQL で日本語入力に対応する
先ほどのやり方だと MySQL で日本語が正しく扱えない状態ですので、日本語対応の環境を作ってみます。
また、タイムゾーンも世界標準時(UTC)となっているので日本時間にしてみます。メモ帳を開いて以下の内容を入力します。
Dockerfile 作成
FROM mysql RUN apt-get update && apt-get install -y locales-all ENV LC_ALL="ja_JP.UTF-8" TZ="Asia/Tokyo"
FROM mysql
は先ほど使っていた mysql のイメージを元にするという意味です。
RUN...の行は世界で使われている言語設定をインストールしています。その中に日本語も含まれています。
ENV...の行は言語設定(ロケール)を日本、タイムゾーンを東京に設定しています。ファイル名は Dockerfile と入力してファイルの種類は「すべてのファイル」としてユーザフォルダに保存します。
Docker ファイルから Docker イメージを作成
コマンドプロンプトを開いて dir コマンドで Dockerfile があることを確認します。
以下のコマンドを実行して、Dockerfile から Docker イメージを作成します。
※もともと用意されているイメージ(mysql)に追加でインストールや設定を行ったもので新しいイメージを作ることができます。Dockerイメージ作成docker build -t mysqlj .※
-t mysqlj
で作成するイメージに名前を付けることができます。(ここでは mysqlj という名前を付けています。)Dockerイメージ作成結果C:\Users\nakaz>docker build -t mysqlj . [+] Building 0.2s (6/6) FINISHED => [internal] load build definition from Dockerfile 0.1s => => transferring dockerfile: 306B 0.0s => [internal] load .dockerignore 0.0s => => transferring context: 2B 0.0s => [internal] load metadata for docker.io/library/mysql:latest 0.0s => [1/2] FROM docker.io/library/mysql 0.0s => CACHED [2/2] RUN apt-get update && apt-get install -y locales-all 0.0s => exporting to image 0.0s => => exporting layers 0.0s => => writing image sha256:9708290e1f00c169ec210361aa1dcebe60744b694b8eb3325bf7cf862e8af7b5 0.0s => => naming to docker.io/library/mysqlj 0.0s作成したイメージを使ってコンテナを起動
作成したイメージからコンテナを起動します。
日本語対応MySQLサーバ起動docker run --name=mysqlj1 -e=MYSQL_ROOT_PASSWORD="password" mysqlj mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci※--name=mysqlj1 でコンテナに名前を付けることができます。ここでは mysqlj1 という名前を付けています。
※mysqld 以降は MySQL で日本語を扱う設定にするためにつけています。日本語動作確認
日本語が入力できるか確認してみます。
MySQLクライアント起動docker exec -it mysqlj1 mysql -uroot -p※コンテナIDの代わりにコンテナ起動時につけた名前を指定することができます。
※bash の部分を mysql にすることでいきなりクライアントを起動することができます。日本語が扱える設定かどうか確認します。
文字コード設定確認
文字コード設定確認show variables like '%chara%';文字コード設定確認結果mysql> show variables like '%chara%'; +--------------------------+--------------------------------+ | 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/ | +--------------------------+--------------------------------+ 8 rows in set (0.02 sec)ちなみに、最初の日本語が扱えない時の設定は以下のようになっていました。
mysql> show variables like '%chara%'; +--------------------------+--------------------------------+ | Variable_name | Value | +--------------------------+--------------------------------+ | character_set_client | latin1 | | character_set_connection | latin1 | | character_set_database | utf8mb4 | | character_set_filesystem | binary | | character_set_results | latin1 | | character_set_server | utf8mb4 | | character_set_system | utf8 | | character_sets_dir | /usr/share/mysql-8.0/charsets/ | +--------------------------+--------------------------------+ 8 rows in set (0.03 sec)※latin1 になっていると日本語が文字化けしたりします。
日本語レコード作成
データベース一覧を確認します。
データベース一覧確認結果mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | mysql | | performance_schema | | sys | +--------------------+ 4 rows in set (0.00 sec)さっき作った testdb がないことがわかります。
これは、コンテナごとに別々のデータ領域が作られるためです。
コンテナは docker run を実行するごとに作成され、 docker ps -a で一覧を確認できます。先ほどと同じように、データベースとテーブルを作って日本語のレコードを挿入してみます。
データベース作成結果mysql> create database testdb; Query OK, 1 row affected (0.04 sec)データベース切り替え結果mysql> use testdb; Database changedテーブル作成結果mysql> create table test_users(id int auto_increment primary key, name varchar(10), birthday date); Query OK, 0 rows affected (0.04 sec)日本語でレコードを挿入してみます。
レコード挿入insert into test_users(name,birthday) values('山田太郎', '1990-01-02');レコード挿入結果mysql> insert into test_users(name,birthday) values('山田太郎', '1990-01-02'); Query OK, 1 row affected (0.04 sec)日本語レコード確認
挿入したデータを確認してみます。
テーブル内容確認select * from test_users;テーブル内容確認結果mysql> select * from test_users; +----+--------------+------------+ | id | name | birthday | +----+--------------+------------+ | 1 | 山田太郎 | 1990-01-02 | +----+--------------+------------+ 1 row in set (0.00 sec)正しく表示されました。
- 投稿日:2021-02-28T16:08:02+09:00
Railsのenumの可読性をあげる
はじめに
Rails(ActiveRecord)でenumを扱おうとすると、db上のデータはintegerとして扱うことが多いと思います。
しかしこれだとdb上のデータが何を意味しているかわかりづらく、可読性が下がります。
そこで今回はMySQLを使うことを前提に、MySQLのENUM型を定義する方法をメモしておこうと思います。環境
ruby 3.0.0
rails 6.1.1
MySQL 5.7手順
class CreateAnimals < ActiveRecord::Migration[6.1] def change create_table :animals do |t| t.string :name t.column :animal_type, "ENUM('dog', 'cat', 'bird')", null: false t.timestamps end end end
t.column :animal_type, "ENUM('dog', 'cat', 'bird')", null: false
この部分で定義しています。
t.column
でMySQLのENUM('dog', 'cat', 'bird')
という構文を直接実行できます。途中からカラムを追加するには少し工夫が必要です。
change_table
で追加できますが、rollbackできないのでdef up
で定義しましょう。class ChangeAnimals < ActiveRecord::Migration[6.1] def up change_table :animals do |t| t.column :animal_type, "ENUM('dog', 'cat', 'bird')", null: false end end def down remove_column :animals, :animal_type end end途中でENUMを拡張
class ChangeAnimals < ActiveRecord::Migration[6.1] def up change_table :attendance_punches do |t| t.change :animal_type, "ENUM('dog', 'cat', 'bird', 'fish')", null: false end end endENUMの型を増やすことはできますが、減らしたり、名称を変更しようとするとエラーになるので注意が必要です。
enumerizeも使えます
animal.rbclass Animal extend Enumerize enumerize :animal_type, in: [:dog, :cat, :bird], predicates: true, scope: true endanimal = Animal.new animal.dog? # => false animal.cat? # => false animal.animal_type = 'dog' animal.dog? # => true animal.cat? # => false Animal.with_animal_type(:bird) # SELECT "animals".* FROM "animals" WHERE "animals"."animal_type" IN ('bird')おわりに
カラム名を
type
にしたらエラーが出てmigrateできませんでした。どうやら予約語になっているらしく、type
をカラム名にすることはできないようです。
- 投稿日:2021-02-28T13:26:56+09:00
EC2からMySQLでRDSに接続するが、「Access denied for user」を突き返される
前提
・単一のVPC内で、EC2インスタンス用のサブネットとRDS用のサブネットを設定している。
症状
$ mysql -h [RDSのエンドポイント] -P 3306 -u root -p Enter password: ERROR 1045 (28000): Access denied for user 'root'@'xx.x.x.xx' (using password: YES)Ruby on RailsのアプリケーションをAWSにデプロイしようとしており、手順を進めていたところ、EC2インスタンスからRDSにMySQLで接続しようとして、このエラーが。
複数のサイトや記事を見てこのコマンドに問題はなさそうだったので、以下の仮説を立てました。
- セキュリティグループが間違っている
- パスワードが間違っている
結論、どちらも不正解でした。
私の場合は不正解でしたが、セキュリティグループが間違っているパターンもあるでしょうし、パスワードが間違っているパターンもあると思います。
では、何が間違っていたのか。
解決
正解はユーザー名でした。
はい、RDSのパスワードを設定するときに同時に設定するマスターユーザーの事ですね。
ご丁寧に、DB インスタンスのマスターユーザーのログイン ID を入力します。
って書いてあるんですけどね。どの記事を見ても下記のように
$ mysql -h [RDSのエンドポイント] -P 3306 -u root -prootと書いてあるので、ここは疑いようもなくrootとしていたわけです。
つまり、これが正解。
$ mysql -h [RDSのエンドポイント] -P 3306 -u [マスターユーザー名] -pまあ、マスターユーザー名を記入する時によく読まなかったのが原因なんですが。
初心者なので、コマンドの意味(-h -P -u -p)を一つひとつ調べて意味を理解していったにも関わらず、なぜかユーザー名だけは疑いもなく打ち込んでいました。なぜなら、rootというユーザーを作成した覚えはなく、RDSを作成したときに自動的に作成されるデフォルトユーザーなのかな的な風に捉えていたからです。
補足
ちなみに、作成した後にこのユーザー名を確認するには、AWSにアクセスし、サービスからRDSを選択します。
確認したいインスタンスを選択し、設定タブを選択。その中に「マスターユーザー名」という欄があります。解決してみて
今回は、かなり時間を費やしてしまいました。ユーザー名が間違っているというところに行き着くまでに丸1日掛かってしまいました(正確には1.5日(;゚∀゚))。挫折するかと思いました。
でも、それだけ悩みに悩んだエラーが解決したときほど嬉しいことはなく、こういうのがあるからどんどん先に進めるんですよね。
いやぁ、プログラミングは面白いなあ。
- 投稿日:2021-02-28T13:06:21+09:00
MySQL global の wait_timeout は、必ずしも session の wait_timeout に適用されない
テストの為に wat_timeout を極端に短く設定したい場面があったのですが、少しまごついたのでメモ。
TL;DR
global の
wait_timeout
の設定を変更すれば、session のwait_timeout
にも適用されると思い込んでいたけど、実際にはinteractive_timeout
の値が適用されたという話。MySQL の variable 設定の、global と session
まずデフォルトの wait_timeout 値。global の設定はこちら。
mysql> show global variables like 'wait_timeout'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | wait_timeout | 28800 | +---------------+-------+ 1 row in set (0.01 sec)一方、session の設定はこちら。同じ値です。
mysql> show variables like 'wait_timeout'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | wait_timeout | 28800 | +---------------+-------+ 1 row in set (0.00 sec)設定変更その1
その場で設定変更すると、即時反映されます。まずは global の設定変更。例では 10秒に変更しています。
mysql> set global wait_timeout=10; Query OK, 0 rows affected (0.00 sec)session の設定を変更するならこちら。
mysql> set wait_timeout=10; Query OK, 0 rows affected (0.00 sec)先ほどと同じく
show variables like 'wait_timeout';
などのクエリで正しく反映されたことが確認できます。設定変更その2(つまづき有り)
設定を固定化する為に、設定ファイル
/etc/mysql/my.cnf
に以下の記述を追記しました。[mysqld] wait_timeout = 10
sudo service mysql restart
でサービスを再起動し、変更した設定が反映されていることを確認します。mysql> show global variables like 'wait_timeout'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | wait_timeout | 10 | +---------------+-------+ 1 row in set (0.01 sec)よしよし。では session の方も確認。
mysql> show variables like 'wait_timeout'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | wait_timeout | 28800 | <--- 28,000 のまま? +---------------+-------+ 1 row in set (0.00 sec)10になってないですね。これが想定外です。global を変更したのだから、session にも適用されるはずだと思っていたのですが、調べてみるとクライアントの実装によっては、必ずしもそうではないようです。で、結論を言うと、私の環境では globalの
interactive_timeout
の設定値が session のwait_timeout
に適用されていました。
改めて設定ファイル/etc/mysql/my.cnf
に以下の通り修正しました。区別がつくように 11秒としています。[mysqld] wait_timeout = 10 interactive_timeout = 11
sudo service mysql restart
でサービスを再起動し、session のwait_timeout
を確認してみると、無事 11が反映されていました。mysql> show variables like 'wait_timeout'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | wait_timeout | 11 | <--- interactive_timeout に設定した 11が適用された。 +---------------+-------+ 1 row in set (0.01 sec)リファレンスにも書かれてる話でした。
https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_wait_timeoutOn thread startup, the session wait_timeout value is initialized from the global wait_timeout value or from the global interactive_timeout value, depending on the type of client (as defined by the CLIENT_INTERACTIVE connect option to mysql_real_connect()). See also interactive_timeout.
まとめ
環境によっては、global の
interactive_timeout
が、session のwait_timeout
に適用される場合がある。
- 投稿日:2021-02-28T09:24:37+09:00
dockerでexpressとmysqlをつなげたい
お品書き
1.はじめに
2.expresss-generatorでアプリの準備
3.Dockerfileとdocker-composeを準備
4.mysqlのコンテナを追加する
5.おわりに1. はじめに
dockerを使ってexpressとMySQLをつなげようとしたときにハマってしまったのでメモしておきます。
dockerを使うと簡単に環境構築ができまた潰すのも簡単です。
つまり初学者こそdockerを使いいろんな環境を構築すべきなんです!
しかしdockerと聞くと難しそうな技術だなと尻込みしてしまいます。
そんな人のためにこの記事を書きました。
難しいことはわかりませんが、dockerを使って開発できるようになりましょう。1-1 目標
dockerを使ってexpress(のコンテナ)とMySQL(のコンテナ)をつなげる
完成コードはこちら1-2 そもそもdockerってなに
パソコン内に仮想環境を作る技術の1つで最近人気になっています。
dockerの良いところは気軽に様々な環境をつくることができることです。
コンテナという仮想環境を作ってそこでやりたい放題できる。
コンテナ内に好きなOSを入れてそれを好きなように操作するって感じです。
基本的は軽量のlinuxベースのもが多いのでlinuxコマンドを知っていると便利です。
そして要らなくなったらそのコンテナを捨てばOKってな感じです。1-3 dockerがわかりにくい理由
とはいったものの、dockerを初めて触ったときに???がいっぱい浮かびました。
仮想環境やコンテナなどの概念などはわかるのですが、実際にどう使えばいいの??的な。
なぜならdockerを扱う上で似たようなコマンドが出てくるし、オプションも多いからです。
特にdockerとdocker-composeが初学者にはややこしい。
一通り調べた結果、とりあえずdocker-composeを使えばなんとかなりそうという結論になりました。(笑)
こちらの動画がかなり参考になりました。
docker-compose入門をチェックしてください。
難しいことは考えずに気軽に開発環境をつくっていきましょう。1-4 今回使うdocker-composeのコマンドは2つだけ
- docker-compose up 複数のコンテナを起動する
- docker-compose down 起動しているコンテナを停止する とりあえずこれだけ知っていれば今回は大丈夫です。 その他のコマンドは後で覚えればOKです。
2. expresss-generatorでアプリの準備
まずはお好きな方法でディレクトリーを作ってください。
今回はdocker_express_mysqlという名前にしておきます。terminalmkdir docker_express_mysql今回はexpress-generatorを使ってサクッとアプリを作っていきます。
アプリの名前はdockerExpressにします。
express-generatorの使い方はこちらを参考にしてください。
express-generatorを使いたいterminal#ディレクトリーへの移動 cd docker_express_mysql #express-generatorを使ってdockerExpressというアプリを作る今回はテンプレートエンジンにejsをつかいます。 express --view=ejs dockerExpress #dockerExpressに移動し立ち上げまで cd dockerExpress npm install npm start次にdockerExpressの中身をいじっていきます。
することは
- nodemonのインストール 保存時に自動で更新してくれるスグレモノ
- mysql2のインストール mysqlに接続するのに必要
- sequelizeのインストール クエリーを簡単にするためのもの。ORMとよばれるものterminalnpm install nodemon mysql2 sequelizepackage.json"scripts": { // "start": "node ./bin/www" <-を下のように変える "start": "nodemon ./bin/www" }これで準備が整いました。
3. Dockerfileとdocker-composeを準備
これはめっちゃ簡単です。というかvscodeの"Docker"というエクステンションを使えば簡単にできます。
詳しいことは省略しますが、Dockerfileはコンテナのもとになるものを作るときに必要なものです。
docker-composeは複数のコンテンなを立ち上げるときに必要になるものです。
この辺の書き方が、初心者には難しいと感じるので、是非本記事で感覚だけでも掴んでください。
とりあえず最低限必要な設定のみにしています。
拡張機能のDockerについて詳しくはこちら3-1 vscode拡張機能"Docker"を導入する
"Docker"という拡張機能を使って作業します。もしvscodeを使っていな人や拡張機能を使いたくない人はあとに出てくるDockerfileとdocker-compose.ymlを自分で作成し、コードをコピーしてください。
導入方法は至って簡単です。
拡張機能から"Docker"と検索してインストールするだけです。
3-2 拡張機能を使う
インストールできたら、拡張機能を使いましょう。
vscodeでcmd+shift+pを押してひたすらenterをおしてください。(詳しく知りたい人はこちら)
そうすると自動でDockerfileとdocker-composeのファイルをができます。
これらの自動生成されたファイルを多少変更していきます。3-3 Dockerfileの編集
まずDockerfileから編集していきます。
いらない設定を削除していきます。DockerfileFROM node:12.18-alpine # ENV NODE_ENV=production ←を削除 WORKDIR /usr/src/app COPY ["package.json", "package-lock.json*", "npm-shrinkwrap.json*", "./"] # RUN npm install --production --silent && mv node_modules ../ ←を下のよう変更する RUN npm install COPY . . EXPOSE 3000 CMD ["npm", "start"]3-4 docker-compose.ymlを編集
続いてdocker-compose.ymlを編集していきます。
こちらは不要な設定を消しておきます。docker-compose.ymlversion: '3.4' services: dockerexpress: image: dockerexpress build: context: dockerExpress dockerfile: ./Dockerfile #environment: ←を削除 #NODE_ENV: production ←を削除 ports: - 3000:3000 #接続するポート番号。お好きな番号でいけます。これでdockerを使う準備が整いました。
3-5 コンテナを立ち上げてみる
試しにコンテナを動かして見ましょう。
注意点はdocker-compose.ymlがあるディレクトリーでdocker-compose upというコマンドを打つことです。terminal#docker-compose.ymlが入っているフォルダに移動してから #コンテナを立ち上げるコマンド docker-compose up
問題なければhttp://localhost:3000/にアクセスできるはずです。
*もしdocker-compose up
をしてエラーが出た場合はコードを確認してdocker-compose build
を行ってからdocker-compose up
を行ってください。ここまででdockerを使ってnodeのアプリをコンテナ内に立ち上げることができました。
4. mysqlのコンテナを追加
ここまで来たらあとはmysqlのコンテナを立ち上げるだけです。
基本的にすることはdocker-compose.ymlにmysqlのコンテナの設定を書くだけです。
今回は最小限の設定でコードを書いていきます。
dockerでコンテナをつくるときには1つのコンテナにすべての機能を入れるのではなくて、最小限の機能に分けて作るのがベストプラクティスらしいです。
なので今回はアプリ部分のコンテナ(expressで作ったもの)とDB部分のコンテナ(今から作るmyslqのもの)に分けて作っています。4-1 docker-compose.ymlにmysqlのコンテナ設定を書く
docker-compose.ymlに次のように追記してください。
ポイントはDBのデータを永続化させるために第3の場所にデータの置き場を作ることです。
詳しくは別の記事で説明は別の記事でできれば。。docker-compose.ymlversion: '3.4' services: # ここから my_mysql: image: mysql #もととなるイメージ。ここではmysql environment: #最小限の記述にしています。他にも環境設定は可能です。 MYSQL_ROOT_PASSWORD: root #rootパスワード。今回はrootにしました。 MYSQL_DATABASE: my_mysql_db #作成するデータベース。今回はmy_mysql_dbにしました。 ports: - '3300:3300' #接続するポート番号。お好きな番号でいけます。 volumes: - my_volume:/var/lib/mysql #第3の場所my_volumeをコンテナ内に同期するコード # ここまで追記 下の部分も忘れずに追記してください。 dockerexpress: image: dockerexpress build: context: dockerExpress dockerfile: ./Dockerfile ports: - 3000:3000 #第3の場所にデータを置いておく場所をつくる。こちらも忘れずに追記してください。 volumes: my_volume:これでmysqlのコンテナを起動する準備ができたので起動させてみましょう。
terminal# docker-composeがあるディレクトリーで docker-compose upこれでエラーがでなかれば起動しているはずです。
4-2 mysqlコンテナ内に入って操作してみよう
一応myslqのコンテナが起動しているか確認しましょう。
イメージとしては先程立ち上げたコンテナ内に入ってmysqlを使うというかたちです。
ここがちょっとややこしいです。とりあえずコマンドを打ちましょう。別のterminalを開く# my_mysqlコンテナ内に入るためのコマンド docker-compose exec my_mysql /bin/bashmy_mysqlのコンテナ内--プロンプトが変わったらコンテナ内に入れた証拠です。続いてmysqlを起動させていきます。 mysql -u root -p --docker-composeで設定したパスワードを入力my_mysqlのコンテナ内show databases;
データベースが作られていることを確認!
*他にもdocker-composeでいろんな指定ができますが省略します。
あとはdockerexpressからmy_mysqlにデータを取りにいけるようにしましょう。4-3 コンテナ間の接続を確認する
expressのコンテナとmysalのコンテナはそれぞれ別のコンテナとして立ち上げました。
なので今は2つのコンテナ立ち上がっているわけです。
それらのコンテナをつなげることに挑戦します。とはいってもとても簡単です。
host名はローカルではlocalhostになっていますが、これをdocker-compose.ymlで設定したDBの名前(今回はmy_mysql)にするだけです。
これを知らなくてかなり時間を無駄にしてしまいました。。。mysql2を使ってクエリを書いても確認できますが、今回はsequelizeを使ってみます。
sequelizeの本家サイトはこちらusersのページに遷移したときにdbにつながっているかをしらべてみます。
なのでroutes内のusers.jsを開き次のようにsequelizeの設定と確かめるためのコードを追記しましょう。users.jsvar express = require('express'); var router = express.Router(); // sequelizeの設定を追加 const { Sequelize } = require('sequelize'); // databaseやuser, passwordをdcoker-compose.ymlで設定したものを使う↓ const sequelize = new Sequelize('my_mysql_db', 'root', 'root', { host: 'my_mysql', // hostの名前をdocker-compose.ymlで設定したmy_mysqlに変更する dialect: 'mysql', }); /* GET users listing. */ router.get('/', async (req, res, next) => { // 忘れずに上に"async"を追加する。 // my_mysqlに接続されているかテスト try { await sequelize.authenticate(); console.log('Connection has been established successfully.'); } catch (error) { console.error('Unable to connect to the database:', error); } res.send('respond with a resource'); }); module.exports = router;URLをhttp://localhost:3000/usersとして、usersページに遷移したときに以下のようなメッセージがターミナルに出たら成功です。
terminaldockerexpress_1 | Executing (default): SELECT 1+1 AS result dockerexpress_1 | Connection has been established successfully. dockerexpress_1 | GET /users 304 45.706 ms - -
とりあえずこれでdockerを使ってexpressとmyqlのコンテナ同士をつなげることができました。
あとはアプリを作り込んだり、DBにデータをどんどん追加していくだけです!5. おわりに
githubにコードを載せておきます。
初学者のため、不十分な理解やコードのミス等があるとおもいます。もしあれば優しく教えてくいただければ幸いです。sequelizeの使い方に関しては後日別の記事を書きたいと思います。
もう少しい詳しいdockerの解説も後日別の記事を書きたいと思います。