20200830のMySQLに関する記事は3件です。

[過去POST][初心者向け]N+1問題が理解できない。.includesを使うとどうなるのか(Railsにて)

過去POST

過去自分がTECH::CAMPのメンターをしていた時期にメモしていた内容を公開します。

N+1問題が理解できない。includes(:user)とする意味がわからない。

class TweetsController < ApplicationController

# 中略

  def show
    @tweet = Tweet.find(params[:id])
    @comments = @tweet.comments.includes(:user)
  end

# 中略

end

で使用している。

これはなにも考えなければ
@comments = @tweet.comments.includes(:user)
の部分は
@comments = @tweet.comments
でもできる。

しかしこれではN+1問題が発生してしまう。
(要は、SQLの発行回数が多くなってしまう。)

【問題の概要】

この文の意味は

詳細を表示したツイートについているコメントのレコードを取得
コメントのレコードに紐つくユーザーレコードの取得

をしている

単純に
@tweet.comments
のように書くと、コメントレコードごとにユーザーレコードを取ってくるという動作をしてしまう。
つまりSQL文でかくと、
(tweetは一旦省略、コメント数は4件、ユーザーは2人とする。)

SELECT 'comments'.* FROM 'comments’

を実行すると

SELECT 'users'.* FROM 'users' WHERE 'users'.'id' = 1 LIMIT 1
SELECT 'users'.* FROM 'users' WHERE 'users'.'id' = 2 LIMIT 1
SELECT 'users'.* FROM 'users' WHERE 'users'.'id' = 2 LIMIT 1
SELECT 'users'.* FROM 'users' WHERE 'users'.'id' = 2 LIMIT 1

のようにcommentsレコードの数だけ、userレコードを取得するクエリが発行される。
これがN+1問題。

では.includes(:user)

をつかうとどうなるのか

SELECT 'comments'.* FROM 'comments'
SELECT 'users'.* FROM 'users' WHERE 'users'.'id' IN (1, 2)

のように一文で終わる。
これがincludesの意味です。
(:user)user_idですね。

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

【備忘録 エラー】MySQL5.7をインストールして起動しようとしたら Can’t connect to local MySQL server through socket ‘/tmp/mysql.sock’ (38)

【生じたエラー】

MySQL5.7をインストールして起動しパスワードを設定しようとしたら
Can’t connect to local MySQL server through socket ‘/tmp/mysql.sock’ (38)
そこから泥沼にハマっていく、、、

【結論】

rm /tmp/mysql.sock

で解決。

【時系列】

ターミナル.
//MySQLをインストール
brew install mysql@5.7

//MySQLを起動させる。
brew services start mysql@5.7
  => Successfully started `mysql@5.7` (label: homebrew.mxcl.mysql@5.7)

※この時は起動できてる。

//パスワードの設定を試みる
mysql_secure_installation
 =>Enter password for user root: 
Error: Access denied for user ‘root’@‘localhost’ (using password: YES)
(本来パスワードの設定以降するはず。なぜか、パスワードの入力画面なる。試しに、macのrootユーザーのパスを入力するも否定。
過去に設定したMySQLのパスも否定。)

https://mamy1326.hatenablog.com/entry/2017/11/27/231450
こちらの記事から、MySQL5.7以降は、初回にランダムなパスワードが設定されていると知り、記事の通り

/var/log/mysqld.log

を確認するも、そのようなファイルは存在せず。

インストールの際に何らかのエラーが生じた可能性を考慮して、一度アンインストールをかけて、再度インストールを試みる。
その後、再度

mysql_secure_installation

を試みるも、

Enter password for user root:  

が表示される。同じように可能性がありそうなパスを入力するも、今度は

Error: Can’t connect to local MySQL server through socket ‘/tmp/mysql.sock’ (38)

このような異なるエラーが表示される。

https://qiita.com/YamKenta/items/a33499a7db7946a43381
こちらの記事を参考に

/usr/local/var/mysql の所有権をユーザーに変えてみても不可。

そもそもhomebrewにエラーが生じていて正しくインストールできていないの
か?と考え、

brew doctor

でチェックする。

image.png

なにやら大量のエラーが、、、

node.jsのインストールの際に、何らかのエラーが生じたと疑いnode.jsを再インストールするためにアンインストールをかけたところ
image (1).png

パーミッションエラー?

sudo rm -rで該当のファイルを全て削除する。

brew doctor

で再度チェック。
image (2).png
指示の元

sudo rm -rf /Library/Developer/CommandLineTools
sudo xcode-select --install
brew cleanup を実行
=> Your system is ready to brew

https://qiita.com/wagi0716/items/94193a80502f9d81a9e0
記事の元、再インストール

us@usnoMacBook-Air list-app % brew services start mysql@5.7
==> Successfully started `mysql@5.7` (label: homebrew.mxcl.mysql@5.7)
us@usnoMacBook-Air list-app % mysql_secure_installation
Securing the MySQL server deployment.
Enter password for user root: 
Error: Can’t connect to local MySQL server through socket ‘/tmp/mysql.sock’ (38)

変化せず。

https://e-joint.jp/639/

こちらの記事を参考にmysqlを完全にアンインストール、インストールするも

us@usnoMacBook-Air ~ % brew services start mysql@5.7
Service `mysql@5.7` already started, use `brew services restart mysql@5.7` to restart.

↑ 初回起動時に、「すでに起動してます」的な表記がされます。

何かアンインストール仕切れずに、過去のMySQLが何らかの影響を及ぼしているのか?

/usr/local/var/mysql/usnoMacBook-Air.pid 

を作成してみたが同様。

us@usnoMacBook-Air ~ % touch /usr/local/var/mysql/usnoMacBook-Air.pid
us@usnoMacBook-Air ~ % mysql_secure_installation             
Securing the MySQL server deployment.
Enter password for user root: 
Error: Can’t connect to local MySQL server through socket ‘/tmp/mysql.sock’ (38)
us@usnoMacBook-Air ~ % sudo chmod 777 /tmp/mysql.sock

で、全てのユーザーに権限を与えてみて

us@usnoMacBook-Air ~ % mysql.server start     
Starting MySQL
... ERROR! The server quit without updating PID file (/usr/local/var/mysql/usnoMacBook-Air.local.pid).

とほほ。
再度アンインストール

us@usnoMacBook-Air ~ % ps aux | grep mysql
us        59108  0.0 0.0 4286716  720 s000 S+  11:56AM  0:00.00 grep mysql

mysqlのプロセスが完全に切れていること確認

us@usnoMacBook-Air ~ % mysql --version
zsh: command not found: mysql

アンインストール済であることを確認

MySQL5.7ではなく最新版のMySQL8をインストールしてみる。

us@usnoMacBook-Air ~ % mysql --version
mysql Ver 8.0.21 for osx10.15 on x86_64 (Homebrew)
us@usnoMacBook-Air ~ % mysql.server start
Starting MySQL
.. ERROR! The server quit without updating PID file (/usr/local/var/mysql/usnoMacBook-Air.local.pid).

↑ エラー文から

us@usnoMacBook-Air mysql % touch usnoMacBook-Air.local.pid

上記ファイルを作成

us@usnoMacBook-Air mysql % ls
(略)
usnoMacBook-Air.local.pid

作成されていることを確認

us@usnoMacBook-Air mysql % mysql.server start          
Starting MySQL
.. ERROR! The server quit without updating PID file (/usr/local/var/mysql/usnoMacBook-Air.local.pid).

上記エラーとなり
再度

us@usnoMacBook-Air mysql % ls

 をしたところ

usnoMacBook-Air.local.pid

が消えてる。もはや若干、心霊現象を疑いつつエラーログ を確認したところ
image (3).png
すでにmysqlが起動している、のようなことが書かれている。
プロセスを確認したところ、起動していないようなだが、、、

マックブックの買い替えも視野入れ始めた所

https://qiita.com/PoohSunny/items/4df890dde4879c2cd29b

↑ の記事を参考に

rm /tmp/mysql.sock

を試して、再度起動してみたら。

us@usnoMacBook-Air mysql % mysql.server start
Starting MySQL
 SUCCESS! 

ん?いけた??

us@usnoMacBook-Air mysql % mysql_secure_installation

パスワードの設定もできました。

【個人的な見解】

おそらく、元々MySQL5.6が入っていたパソコンに、さらにMySQL5.7をインストールしたため(ここでアップデートを行うべきであった。)mysql.sockが5.6の設定のままであった。

本来MySQLを起動時に適切mysql.sockが作成されるが、すでに存在しているため(5.6インストール時の)mysql.sockが作成されず、MySQL5.7とmysql.sockに何らかのズレが生じててエラーとなっていた。
MySQLをアンイストールしてもmysql.sockは消去されないため、再インストールで解決しなかった。

と、個人的には思います。

ハマると疲れますが、無理やりにでも調べるので理解が深まる(ような気がします)。

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

【Rails環境構築】Docker + Rails + MySQL (初心者も30分でOK!)

Dockerを使った環境構築

「環境構築はコードを書くよりも難しい」と聞いたことがあります。

Dockerは簡単!

何故かと言うと、、、
コピペするだけでいいから
ただし、理解するまで少し時間がかかるかも、、、
まずは簡単な概要から

Docker

仮想環境を構築するための道具

コンテナ

仮想環境そのもの
(DockerはDockerエンジンの上にコンテナが動く)

イメージ

Dockerコンテナを実行するために必要なもの。

環境構築の手順

①.プロジェクト(ディレクトリ)を作成し、移動する

$ mkdir アプリ名
$ cd アプリ名

②.Dockerfile、docker-compose.yml、Gemfile、Gemfile.lockを作成する

$ touch Dockerfile docker-compose.yml Gemfile Gemfile.lock

③.エディタを開き、Dockerfile、docker-compose.yml、Gemfileを下記からコピペする

Dockerfile

Dockerのイメージを自動で生成してくれるファイル

FROM ruby:2.5
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs

RUN mkdir /myapp
WORKDIR /myapp

COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock

RUN bundle install
COPY . /myapp

FROM : 使用するイメージとコマンド
RUN : コマンドの実行
WORKDIR : 作業ディレクトリの設定
COPY : コピー元(ホスト側)とコピー先(仮想環境側)のファイルまたはディレクトリを指定

Gemfile

source 'https://rubygems.org'
gem 'rails', '~> 5.2.3'

docker-compose.yml

version: '3'

services:
    db:
        image: mysql:5.7
        environment:
            MYSQL_USER: root
            MYSQL_ROOT_PASSWORD: password
        ports:
            - "3306:3306"
        volumes:
            - ./db/mysql/volumes:/var/lib/mysql

    web:
        build: .
        command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
        volumes:
            - .:/myapp
            - gem_data:/usr/local/bundle
        ports:
            - 3000:3000
        depends_on:
            - db 
        tty: true
        stdin_open: true

 volumes:
 gem_data:

version : docker-composeのバージョン。
depends_on : 依存関係を示していて起動順を制御できる。ここでは「db→web」へ起動する。

Docker composeとは

Docker composeとは、複数のコンテナから成るサービスを構築・実行する手順を自動化し、管理を容易にする機能。Docker composeでは、composeファイルを用意してコマンドを一回実行することで、そのファイルから設定を読み込んですべてのコンテナを起動することができる。

serviceについて

Docker composeでは、アプリケーションを動かすための各要素をserviceと呼んでいる。通常はweb(rails)とdb(mysql)と名付ける。

rm -f tmp/pids/server.pidについて

pidとはプロセスIDのことである。pidは、開発用Webサーバーを起動するときにtmp/pids/server.pidに書き込まれ、終了するときに削除される。server.pidにpidが書かれているとサーバーが起動中と判断されてしまう。

portsについて

ports:
  - 3000:3000

上記は、コンテナ内のポート3000番をホストの3000番にマッピングするという意味。これにより、コンテナ内のWebサーバーへhttp://localhost:3000でアクセスできるようになる。

volumeについて

volumes:
   - ./db/mysql/volumes:/var/lib/mysql

は、ホストの./db/mysql/volumesをコンテナ内の/var/lib/mysqlにマウントするという意味。簡単にいうと、Dockerのコンテナと、ローカルを同期している。

volumes :
   - .:/myapp

.がホストのディレクトリ全てを意味し、それをコンテナ内のmyappにマウントしている。

④.下記コマンドを実行する

$ docker-compose run web rails new .--force --database=mysql --skip-bundle

runコマンドではimageの構築から、コンテナの構築・起動までしてくれる。
引数にサービスを指定する必要あり。
docker-compose.ymlのvolumes:- .:/myappの部分でdockerとローカルのディレクトリを同期するように設定しているため、このコマンドを実行後、同時にローカルにも同様のファイルが生成される。

⑤.config/database.yml内の(password: password、host: db)に変更する

default: &default
   adapter: mysql2
   encoding: utf8
   pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
   username: root
   password: password   #passwordを追記しました
   host: db         #localhostからdbに変更しました

⑥.下記コマンドを実行する

$ docker-compose build

Gemfileが更新されたときbuildを実行する

⑦.下記コマンドを実行する

$ docker-compose up -d

docker-compose.yml通りにコンテナが起動する。
docker-compose.ymlの変更を反映させる際にも実行。
-dオプションはバックグラウンド起動。

⑧.下記コマンドを実行する

$ docker-compose run web rails db:create

データベースを作成する。
docker-compose run webでローカルからコマンドを実行できる。(コンテナに入る必要はない)

⑨.下記コマンドを実行し、2つのコンテナが立ち上がっているか確認する。(完成!)

$ docker ps

現在起動しているコンテナを表示するコマンド。
localhost:3000にアクセスするといつものYay!You're on Rails!が表示される。

 コンテナを削除したい場合は下記を実行する

$ docker-compose down

docker psでコンテナが削除されているか確認する。
またコンテナを生成・起動する場合はdocker-compose up -dを実行する。

まとめ

かなり簡単だったんじゃないかなと思います!
これでアプリ開発していきましょう!:thumbsup:

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