- 投稿日:2020-08-30T18:41:24+09:00
[過去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
ですね。
- 投稿日:2020-08-30T16:38:00+09:00
【備忘録 エラー】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でチェックする。
なにやら大量のエラーが、、、
node.jsのインストールの際に、何らかのエラーが生じたと疑いnode.jsを再インストールするためにアンインストールをかけたところ
パーミッションエラー?
sudo rm -rで該当のファイルを全て削除する。
brew doctorsudo rm -rf /Library/Developer/CommandLineTools sudo xcode-select --install brew cleanup を実行 => Your system is ready to brewhttps://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)変化せず。
こちらの記事を参考に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 mysqlmysqlのプロセスが完全に切れていること確認
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が消えてる。もはや若干、心霊現象を疑いつつエラーログ を確認したところ
すでに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は消去されないため、再インストールで解決しなかった。と、個人的には思います。
ハマると疲れますが、無理やりにでも調べるので理解が深まる(ような気がします)。
- 投稿日:2020-08-30T09:52:24+09:00
【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 . /myappFROM : 使用するイメージとコマンド
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-bundlerunコマンドでは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 buildGemfileが更新されたとき
build
を実行する⑦.
下記コマンドを実行する
$ docker-compose up -ddocker-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
を実行する。まとめ
かなり簡単だったんじゃないかなと思います!
これでアプリ開発していきましょう!