20200603のMySQLに関する記事は8件です。

MySQL エラーコードの意味を出力する

目的

  • MySQLにて数字にて出力されるエラーコードの意味を出力するコマンドを紹介する

実行例

  • 下記にエラーコードの意味を出力するコマンドを記載する。

    $ perror エラーコードの数字
    

具体例

  • エラーERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/lib/mysql/mysql.sock' (111)のエラーコードの意味を出力する場合は下記コマンドを実行する。

    $ perror 111
    >OS error code 111:  Connection refused
    
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Flask の MySQL 接続と Life Cycle

Flask の MySQL 接続と Life Cycle

mysql has gone away が現れるようになり、前から気になってたflask appのlife cycleについて理解しようとしてみた。
結局原因が途中でわかったので尻すぼみだけど、記録として残しておく。

わかってないこと

  • appはいつ起動していつ終わるのか?
    • urlに対する1 requestで app起動、コンテンツ返して終了、と思っていたが、gone awayってことはconnectionをkeepしている可能性がある
  • MySQL接続はいつ切れるのか?
    • MySQL(app) ではない独自の接続を多数持っている。これらは切断されないのか?
    • 元のinstanceが廃棄されれば切断されると思うが、廃棄されているのか?

きれいなflaskは毎回切断している

以下のコードで検証

from flask import Flask
from flask_mysqldb import MySQL

app = Flask(__name__)


app.config['MYSQL_USER'] = 'report_local'
app.config['MYSQL_PASSWORD'] = 'report_local'
app.config['MYSQL_HOST'] = 'sys-test-lowusage-db001-dbs4dev-jp2v-dev.lineinfra-dev.com'
app.config['MYSQL_DB'] = 'mysql'
app.config['MYSQL_PORT'] = 20306
app.config['MYSQL_CURSORCLASS'] = 'DictCursor'

mysql = MySQL(app)

@app.route('/')
def users():
    cur = mysql.connection.cursor()
    cur.execute('''SELECT CONNECTION_ID(); ''')  # mysql connection idを取得
    rv = cur.fetchall()
    return "app obj id = %s, %s" % (id(app), str(rv))   # <----- appのobject idも返す

if __name__ == '__main__':
    app.run(debug=True)

参考 https://github.com/admiralobvious/flask-mysqldb

結果

  • appは作り直されない

- MySQLは切断されていることがわかった

# first time
app obj id = 4424594128, ({'CONNECTION_ID()': 7196},)

# second time
app obj id = 4424594128, ({'CONNECTION_ID()': 7197},)

# and so on...
app obj id = 4424594128, ({'CONNECTION_ID()': 7198},)

appが作り直されないのは、app自体はあくまで初回の python run.py をした時点 = Web Serverを起動した時点で同時に生成されるから・・なんだろうか。

自分のいけてない MySQLdb で検証

show processlist; を見ていたら、やっぱり自分のflaskは切断してなかった。大量にコネクションが残ってた。

webサーバとアプリサーバが完全にわからない

appが作り直されない理由がよくわからない。

これは、根本的にwebサーバを理解していないのが問題なんだと思う。
webサーバは、起動するとソケット(ポート)をlistenにして接続を待ち受ける。
リクエストが来たら、定められた位置にあるファイルを取って渡すだけだ。
そこにあるのがアプリなら、アプリを起動する・・・・そう思っていけれど。

web serverとpython applicationの間は、どうなっているんだろうか?
web server processを造ったとき、同時に受けてであるアプリも作られる。

アプリ側も待機しないといけない。socketかportで、web-serverからのリクエストに答えるために
ずっと起動してlistenし続けている。これがアプリサーバか。
pythonのhttp serverだと、全部ひとつで完結していて、その結果アプリサーバが起動する・・・
当然、起動したときのものはずっと残る・・・
single threadの場合は、そのアプリが永遠に行き続ける・・のだろうか。
multi threadだったら? アプリサーバは、あくまでメインスレッドが起動するだけ?
リクエストに応じてappが作られる?むー・・それだとつじつまが合わない気がする

simple な web serverで検証

たぶんflaskのdev serverはこれを使ってるんじゃなかろうか。

import http.server
import datetime


class App():
   def show(self):
       return "Hello %s" %  datetime.datetime.now()

app = App()

class myHandler(http.server.BaseHTTPRequestHandler):
    def do_GET(self):
        self.send_response(200)
        self.send_header('Content-type','text/html')
        self.end_headers()
        self.wfile.write(str.encode("app id=%s, %s" % (str(id(app)), app.show())))
        return

server_address = ("", 8000)
simple_server = http.server.HTTPServer(server_address, myHandler    )
simple_server.serve_forever()

参考 https://docs.python.org/3/library/http.server.html#http.server.HTTPServer

で、結局原因はclass変数だった

appが作り直されない理由がよくわからない。

これを書いて数週間経った今の理解で行くと、appインスタンスは作り直される。インスタンスは作り直されるが、app クラス は最初に作ったときのままだ。
pythonではクラス変数は最初の起動から永遠に再利用される。
今回の原因は、appから呼び出している自作クラスのmysql connection poolをいれる変数がclass変数だった。

class MyClass:
    dbpool = {}        # ←これが原因

    def __init__(self):
        self.dbpool = {}     # ←インスタンス変数にして解決した

class変数はアプリが最初に起動された状態を永遠に保持するので、コンテンツを返した後も残り続ける。
自分のclassは、self.dbpoolの中にconnectionオブジェクトが残っている限り再利用する構造にしていたので、
オブジェクトは残るが、オブジェクト自体は数時間で接続が切れている、という状態だった。
切れている接続を再利用すると mysql has gone away になる。

いま考えれば con.close() する時に self.dbpool の中を空にしていなかったのかもしれない。

いずれにしても、インスタンス変数にした結果、アクセスのたびに self.dbpool が初期化されるようになったので
古いconnectionオブジェクトを再利用することはなくなった。そういえばself.dbpoolを空にする修正も一応した気がしてきた。

個人的には ↓ に近いような問題だと思っていて、いや普通なのかもしれないけど、pythonなんだかなぁと感じたりしている。

おしまい

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

MAMPのMySQLを利用するとことで面倒な環境構築せず、すぐにMySQL使えるように設定しよう!

まず、MAMPをダウンロードします。(無料)
https://www.mamp.info/en/downloads/
MAMP&MAMP PROをクリックしてダウンロード。

ダウンロードしたら、インストールします。
インストール終了したら、アプリケーションに作成されます。
スクリーンショット 2020-06-03 19.51.15.png

MAMPアイコンをMacのDockにドラッグ&ドロップしてショートカットの作成をオススメします。
アイコンをクリックすると起動します。
スクリーンショット 2020-06-03 19.54.13.png

Start Serversをクリックすると緑色に点灯してMySQLが使えるようになります。
※Cloudは、赤く点灯しますが、無視してください。
スクリーンショット 2020-06-03 19.56.15.png

MySQLが起動すると以下のphpMyAdminリンクにアクセスできます。
http://localhost:8888/phpMyAdmin/
phpMyAdminは、データベース、テーブル、カラムの作成、変更、削除が容易にできます。

laravelの.envを設定しましょう。
デフォルトは、以下になっています。
スクリーンショット 2020-06-03 20.06.28.png

スクリーンショット 2020-06-03 20.08.45.png

DB_DATABASE= データベース名を記述。
DB_PASSWORD= Macの場合は、rootになります。
追加でDB_SOCKET=/Applications/MAMP/tmp/mysql/mysql.sockを記述。

phpMyAdminでデータベースを作成してみましょう。
新規作成をクリック。
データベース名を入力。
作成ボタンをクリックでデータベースが作成されました。
スクリーンショット 2020-06-03 20.15.07.png

作成したデータベース名を.envDB_DATABASE= に記述。
スクリーンショット 2020-06-03 20.08.45のコピー.png

ターミナルでmigrationします。

php artisan migrate

設定したデータベースにmigrationされました。

phpMyAdminにアクセスして確認。
http://localhost:8888/phpMyAdmin/
作成したデータベース以下にmigrationされています。

以上です。

参考になったら幸いです。

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

Rails gem deviseって?

Rails gem devise について

Ruby on Rails を学習中、deviseというユーザーも新規登録、ログイン機能などがgemをインストールするだけで簡単に作れちゃうというもの。非常に優れものです。

投稿主の備忘録もかねて手順を簡単に紹介していきます。

deviseをインストールする

Gemfile
gem 'devise'

ここで注意すべきなのはdeviceではなくdevise

私がプログラミング超初心者だった時このスペルミスでエラーが出てしまいました。

ここでgemをインストールするのでコマンドラインで$ bundle installします

また、devise用のインストールコマンドがあるので実行します。

ターミナル
$ rails g devise install

ここまででdeviseのインストールが完了します。

deviseのモデルを作成する

マイグレーションファイルに記載した情報を基にデータベースの型、制約を設定していきます。

ターミナル
$ rails g devise user

普段Railsアプリケーションでモデルを作成していく時には $ rails g model userというコマンドを打てばモデルの作成ができますがここではdeviseコマンドでモデルを作成します。

このコマンドを実行後model/user.rbとuser用のマイグレーションファイルが作成されていれば成功です。

ユーザーのマイグレーションファイルには

20200603_devise_create_users.rb
class DeviseCreateUsers < ActiveRecord::Migration[5.0]
  def change
    create_table :users do |t|
      ## Database authenticatable
      t.string :name,               null: false
      t.string :email,              null: false, default: ""
      t.string :encrypted_password, null: false, default: ""

      # 〜省略〜
    end

    add_index :users, :name, unique: true
    # usersの情報全てに検索がかけれるようにadd_indexを貼る

nameカラム、eーmailカラムなどが設定され、それぞれnull: falseで制約がかけられておりnullであればユーザーの新規登録でエラー表示されるようになります。

user.rbにはバリデーションもかけておきましょう。

user.rb
class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable,  :validatable

  validates :name, presence: true, uniqueness: true
   # nameカラムが空でないこと、重複したnameは登録できないようにバリデーションをかけている

end

バリデーションには様々な制約を設けることができ、もっと知りたいという方はこちらから!
https://qiita.com/h1kita/items/772b81a1cc066e67930e

これでデータベースと紐づくマイグレーションファイルとモデルの設定が完了しました。

ターミナル
$ rails db:migrate

マイグレーションファイルをマイグレートしておきましょう。

ビューファイルの準備

ビューファイルもdeviseコマンド一発で作成できます。

ターミナル
$ rails g devise:views users

これで簡単ではありますがユーザーの新規登録とログイン機能の実装ができました。

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

パンくず機能

自分の環境

Ruby 2.5.1p57
Rails 5.2.4.2
mysql2 (0.5.3)

なぜパンくずを実装しようと思ったか

プログラミングスクールでフリーマーケットアプリケーションを作った際に
どこのページにいるかを目視できるようにする為です。

パンくずという言葉の由来

パンくずリストという語源は、童話「ヘンゼルとグレーテル」の話の中に主人公の兄弟が森に入るときに、迷子にならないように自分たちが通ってきた道にパンくずを置いていったエピソードが由来となっています。

パンくずのメリット

ユーザビリティが高くなる

パンくずリストを設置することで「自分が今サイト内のどこにいるのか」や「サイトの構造」を認識しやすくなり、結果として使いやすさを高めることができます。

パンくずリストとは

このようなことを指します。
Image from Gyazo

① パンくず機能のgem導入

Gemfileの一番下にgem gretelを記述

Gemfile.
gem gretel

ターミナルでbundle installをする

$bundle install

② ファイルの設定

ターミナルで実行する

$ rails generate gretel:install

実行すると下記のファイルが生成されます

config.breadcrumbs.rb
crumb :root do
  link "トップページ", root_path
end

crumb :loginer do
  link "ログインページ", new_user_session_path
end

・ :loginer ← 設定ファイルを呼び出します。
・ "ログインページ" ← パンくずリストに表示される名称です。
・ new_user_session_path ← 呼び出し元のパスを指定します。

③ ビューファイルの設定

new.html.haml
    -# config/breadcrumbs.rbに定義したmypageを呼び出し
    - breadcrumb :loginer
    -# 下記を記述した箇所にパンくずリストが表示される。
    = breadcrumbs separator: " &rsaquo; "

・ separator ← パンくずの区切り文字を指定。「&rsaquo」は出力されると「›」になります。

パンくずリストの表示は、一般的に全ページに表示する事ができるようlayout/breadcrumbs.html.hamlに記載する事が多いと思いますが、今回は全部のページではなく一部のページに投稿させたいので、部分テンプレート化して呼び出しています。

layout/_breadcrumbs.html.haml
.breadcrumbs
  = breadcrumbs pretext: "",separator: " &rsaquo; ", class: "breadcrumbs-list"



実際に記入するとこのように表示されます

new.html.haml
    - breadcrumb :loginer
    = breadcrumbs separator: " &rsaquo; "

Image from Gyazo

④ 親の設定

config.breadcrumbs.rbcrumbendの間にparentを設定する事で親を設定する事ができます。商品詳細ページを親に設定して商品編集ページをリストに表示させましょう。

config.breadcrumbs.rb
crumb :root do
  link "トップページ", root_path
end

crumb :shower do
  link "商品詳細ページ", item_path
end

crumb :editer do
  link "商品編集ページ", edit_item_path
  parent :shower
end

するとこのような表示になります。
Image from Gyazo



以上がパンくず機能の実装のやり方です。



今回参考にさせていただいた記事
https://qiita.com/Azure0701/items/16de34a0010eb7f05d89
https://www.asobou.co.jp/blog/web/breadcrumb-list
https://qiita.com/you8/items/d2d37a745060b79c112f

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

Rails Herokuデプロイ手順

プログラミング初学者のため訂正がありましたらご指摘ください。
gitをインストールしている前提です。

自身の環境

  • Ruby 2.5.1
  • Ruby on Rails 5.2.4.1
  • MySQL (gem 'mysql2', '>= 0.4.4', '< 0.6.0')

デプロイの流れ

  1. The Heroku CLIの設定
  2. Herokuにログイン
  3. Herokuにデプロイ

Herokuとは

HerokuとはWebアプリケーションを簡単に全世界に公開できるクラウドプラットフォームです。

参考)HEROKU とは

以下のURLからHerokuのユーザー登録を行います。ユーザー登録は無料です。

https://signup.heroku.com/jp

1. The Heroku CLIの設定

The Heroku CLIをインストールすることで、Herokuのコマンドが使えるようになります。

下記のリンクからOSを指定してダウンロードしてインストールを完了させてください。

https://devcenter.heroku.com/articles/heroku-cli

2. Herokuにログイン

The Heroku CLIをインストールしたので、ターミナル上でHerokuのコマンドが使えるようになりました。

早速ターミナルからHerokuにログインしましょう。

Herokuへアップロードしたいアプリのディレクトリへ移動し、「heroku loginコマンド」を実行してください。

loginコマンド実行後、herokuに登録したメールアドレスとパスワードの入力が必要です。

$ cd app # appの部分を自分の作ったアプリ名にします
$ heroku login # herokuにログインする
Enter your Heroku credentials:
Email:

メールアドレスとパスワードの入力が完了すると以下のように表示されます。

Logged in as 入力したメールアドレス

3. Herokuにデプロイ

HerokuではPostgreSQLデータベースを使います。

なので、PostgreSQLをインストールしていきます。

以下のコマンドをターミナルで実行します。(既にインストールされている方はインストールしなくて大丈夫です。)

$ brew install postgresql

インストールが完了したら、本番 (production) 環境にpg gemをインストールしてRailsがPostgreSQLと通信できるようにします。

以下のコードをGemfileの最下部に追加してください。

Gemfile.
group :production do
  gem 'pg'
end

pg gemは本番用のgemでローカル環境にはインストールしないようにします。その場合、bundle installに--without productionを追加します。このフラグを追加することで、pg gemはローカル環境には反映されないようになります。それでは以下のコマンドを実行します。

$ bundle install --without production

bundle installの本番環境用

次に「heroku create アプリ名」コマンドでheroku上にアプリケーションを作成します。 以下のコマンドを実行します。

$ heroku create

上記のようにアプリ名を入力しないと自動で名前をつけてくれます。
一度登録した名前は使えないので注意してください

上記のコマンドを実行すると、以下のような結果が表示されます。

Creating app... done, ⬢ app(アプリ名)
https://app(アプリ名).herokuapp.com/ | https://git.heroku.com/app(アプリ名).git

https://~~.herokuapp.com/が上記のコマンドで作成されたサブドメインです。 この時点でブラウザに表示可能ですが、今はまだ何もありません。デプロイしてWebページを表示させましょう。

RailsアプリケーションをHerokuにデプロイするには、まずGitを使ってHerokuにリポジトリをプッシュします。

$ git add .
$ git commit -m "initial commit"
$ git push heroku master

上手く行くと、下記のようにremote: Verifying deploy... done.と表示されます。

.
.
.
remote: Verifying deploy... done.
To https://git.heroku.com/app(アプリ名).git
 * [new branch]   master -> master

次に以下コマンドでmigrationを実行します。ローカル環境で行なっていたrails db:migrateのコマンドを本番環境でも行うというイメージです。

$ heroku run rails db:migrate

上記のコマンドを実行したら、以下のコマンドを実行してWebページを表示させましょう。

$ heroku open

以上です。

参考記事

https://qiita.com/kazukimatsumoto/items/a0daa7281a3948701c39
https://qiita.com/NaokiIshimura/items/eee473675d624a17310f

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

MySQL5.7でアプリが動かなくなる原因の1つ

MySQLのバージョンを5.7にあげたら動かなくなった

MySQL8がリリースされているものの、まだまだMySQL5系をご利用されている場合は多いかと思います。今回はphpのアプリで利用しているMySQLのバージョンを5.6→5.7に入れ替えたときに急にアプリが動かなくなりました。

原因:sql_modeの設定

原因はMySQLの設定項目であるsql_modeでした。旧環境(5.6)と新環境(5.7)で
この設定が異なっていたため、SQLエラーが頻発していました。
(sql_modeの解説は他に詳しく書いている方が多くいると思うので省略します。)

コマンド実行で変更可能ですが運用的には/etc/my.cnf

[mysqld]
...
sql_mode=NO_AUTO_VALUE_ON_ZERO,STRICT_ALL_TABLES

みたいに明示的に指定して固定しておいたほうが間違いないかと。

sql_modeのデフォルト設定は5.7以降よく変わっているようです。(参考

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

MySQL 5.7で文字列末尾から特定の文字列が出現するまでの文字列を抽出する

TL;DR

とある案件で、URLを含む本文から本文末尾に出現するURLのみ抽出して本文とURLを分割してほしいと依頼が来たので、文字列末尾から特定の文字列が出現するまでの文字列を抽出するクエリを作成しました。

SELECT RIGHT(body, INSTR(REVERSE(body), REVERSE('http://')) + CHAR_LENGTH('http://') - 1) AS url FROM articles;

環境

  • MySQL 5.7

使用する関数

名前 説明
INSTR 部分文字列が最初に出現する位置のインデックスを返します
REVERSE 文字列内の文字を逆順に並べ替えます
RIGHT 右端から指定された数の文字を返します

引用元: MySQL 5.6 リファレンスマニュアル / 関数と演算子 / 文字列関数 1

クエリ解説

サンプルのテーブル

mysql> SELECT * FROM articles;
+----+---------------------------------------------------+
| id | body                                              |
+----+---------------------------------------------------+
|  1 | 本文http://example.com 本文http://example.com      |
+----+---------------------------------------------------+
1 row in set (0.00 sec)

検索したい文字列が1文字の場合

  1. REVERSE関数で本文の文字列を逆順に並べ替え、INSTR関数で検索文字列が最初に出現する位置のインデックスを取得
  2. 1.で取得したインデックス本文の文字列末尾から検索文字列が出現するまでの文字列の長さになるので、そのままRIGHT関数に渡す
mysql> SELECT INSTR(REVERSE(body), 'h') AS `index` FROM articles;
+-------+
| index |
+-------+
|    18 |
+-------+
1 row in set (0.00 sec)

mysql> SELECT RIGHT(body, INSTR(REVERSE(body), 'h')) AS url FROM articles;
+--------------------+
| url                |
+--------------------+
| http://example.com |
+--------------------+
1 row in set (0.01 sec)

検索したい文字列が2文字以上の場合

  1. REVERSE関数で本文の文字列検索したい文字列を逆順に並べ替え、INSTR関数で検索文字列が最初に出現する位置のインデックスを取得
  2. 1.で取得したインデックス本文の文字列末尾から検索文字列の末尾が出現するまでの文字列の長さになるので、検索文字列の長さを足して1引いてからRIGHT関数に渡す
mysql> SELECT INSTR(REVERSE(body), REVERSE('http://')) AS `index` FROM articles;
+-------+
| index |
+-------+
|    12 |
+-------+
1 row in set (0.00 sec)

mysql> SELECT RIGHT(body, INSTR(REVERSE(body), REVERSE('http://')) + CHAR_LENGTH('http://') - 1) AS url FROM articles;
+--------------------+
| url                |
+--------------------+
| http://example.com |
+--------------------+
1 row in set (0.01 sec)

まとめ

実際に使ったときはCASE式とサブクエリを使って、本文とURLを分割して別々のカラムに保存しました。
開発や運用の都合で、ときどきこの手の泥臭いクエリが必要になることがあるので、文字列操作の関数は一通り目を通しておこうと思いました。

追記(2020/06/03)

とある先輩エンジニアに今回のケースならSUBSTRING_INDEX関数で取得できるとアドバイスを貰ったので追記します。

SELECT CONCAT('http://', SUBSTRING_INDEX(body, 'http://', -1)) AS url FROM articles;

SUBSTRING_INDEX関数は区切り文字が指定された回数出現する前の部分文字列を返しますが、第3引数に負の数を指定すると文字列の末尾から検索します。

mysql> SELECT SUBSTRING_INDEX(body, 'http://', -1) FROM articles;
+--------------------------------------+
| SUBSTRING_INDEX(body, 'http://', -1) |
+--------------------------------------+
| example.com                          |
+--------------------------------------+
1 row in set (0.00 sec)

あとはCONCAT関数で区切り文字列と結合するだけですね。

mysql> SELECT CONCAT('http://', SUBSTRING_INDEX(body, 'http://', -1)) AS url FROM articles;
+--------------------+
| url                |
+--------------------+
| http://example.com |
+--------------------+
1 row in set (0.01 sec)

  1. MySQL 5.7のリファレンスは日本語化されていなかったので5.6にしておきました。 

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