恥知らずのウェブエンジニア -web engineer, shameless

これは一歩を踏み出すことができない者たちのブログ

サービス稼働中に不要なDBのテーブルを安全に削除する方法

担当サービスのDBサーバーのディスク容量の空きが少なくなり、不要なテーブルを削除することになりました。
対象のテーブルのデータ量が多く、平常時でもそこそこ負荷のあるサーバーなのですが、
テーブル削除だけのためにメンテナンスをするのもアレなので、
深夜帯にしれっとdropしたい気持ちを抑えつつ、負荷のかからないdrop tableの手順を試してみました。

サービス稼働中にテーブルdropする時の問題点

metadata lock

drop対象のテーブルに書き込みやトランザクションが貼ってあるとmeta data lockがかかってしまいます
今回は参照等がないテーブルなので、問題なし

巨大なファイルを削除するとI/O待ちが発生する

巨大なテーブルのデータファイルを削除すると他のプロセスがI/O待ちになる可能性があります

安全にテーブルdropする方法やってみた

完全にこちらを参考をさせて頂きました。

qiita.com

dropする前にデータファイルにハードリンクを貼り、
drop時はファイル削除を行わず、後に優先度を下げて実ファイルを削除するという流れです。


さっそく本番でもやってみました。

# データファイルにハードリンクを張る
ln /usr/local/mysql/data/hoge_db/bk_hoge_tbl.ibd /home/hoge/bk_hoge_tbl.ibd


time mysql  -e "DROP TABLE bk_hoge_tbl"
real    0m0.047s
user    0m0.002s
sys     0m0.002s


time ionice -c 3 rm -f /home/hoge/bk_hoge_tbl.ibd
real    0m14.505s
user    0m0.001s
sys     0m0.065s

確かに意図通り、dropはデータ量に限らず一瞬で終わり、
データ削除はデータ量によって実行時間に違いがありました。

が、、、drop中は問題ありませんでしたが、データ削除中に若干レスポンスの悪化が見られました。


巨大なファイルを低負荷で削除する方法

結局、巨大なファイルを削除する時のI/O待ちが問題なら
drop前にパーティション切ってデータ細切れにするのはどうかな?でもめんどいなーって思っていたら下記の記事を見つけました。

www.nari64.com

巨大なファイルを削除する方法

このケースが一番悩まされました。しかし、最近 truncate コマンド使えばいいのでは〜ということに気がつきました。

truncate コマンドを用いて少しずつファイルサイズを減らす
sleep をはさむ


徐々にファイルサイズを小さくして、削除すればI/O負荷軽減できそうです。
次回はこちらの手順で削除してみようかと思います♪





感謝致します。

f:id:ogataka50:20150922171405j:plain

mysql explain で出てくるSelect tables optimized awayって何ですか?

日次で開発環境で実行されたクエリにEXPLAINをかけて、危なそうなクエリを通知するようにしています。 そこでこんなんが出てきました

mysql> EXPLAIN PARTITIONS SELECT MAX(`hoge_col`) AS total_count FROM `hoge_table`;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+------------------------------+
| 1 | SIMPLE | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | Select tables optimized away |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+------------------------------+

index貼られてないー><

index貼る

mysql> EXPLAIN PARTITIONS SELECT MAX(`hoge_col`) AS total_count FROM `hoge_table`;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+------------------------------+
| 1 | SIMPLE | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | Select tables optimized away |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+------------------------------+

変わらず・・・

そこでよくよく確認すると、hoge_colはprimary_keyになっていました。

で、今度は「Select tables optimized away」はなんだと。

ドキュメントを確認すると、

MySQL :: MySQL 5.6 リファレンスマニュアル :: 8.8.2 EXPLAIN 出力フォーマット

Select tables optimized away

クエリーにはすべてインデックスを使用して解決された集約関数 (MIN()、MAX())、または COUNT(*) のみが含まれていますが、GROUP BY 句は含まれていませんでした。オプティマイザは 1 行のみを返すべきであると判断しました。

オプティマイザが良きにはからってくれた結果だそうです。 内部的にはインデックスを使っているようなので、負荷等は問題なさそうです。ほぼ最速の模様。



感謝致します。 f:id:ogataka50:20150808232205j:plain

1分でできる!mysqlslapでDBのかんたん性能調査、ベンチマーク

業務中にそこそこ大きなスキーマ変更をすることになり、実際パフォーマンスに変化があるかベンチマークを取る必要が出てきました。


jmeterとかでごりょごりょとかしないとなのかーとか思ったのですが、 今回はスキーマ変更のみなので、単純にDBのみの測定でよいので、なんかいいツールはないかと探したところ、

mysqlslapなるものがあるようなので試してみました


MySQL :: MySQL 5.7 Reference Manual :: 4.5.7 mysqlslap — Load Emulation Client

MySQLクライアント負荷エミュレーション mysqlslap を使う - Qiita

mysqlslapはMySQLサーバのクライアント負荷をエミュレートし、各ステージのタイミングを報告する診断プログラムです。サーバにたいして複数のクライアントがアクセスしているかのように作動します。mysqlslapはMySQL 5.1.4.から提供されています。

さっそく試してみーる

使い方

mysqlがインストールされていれば標準でmysqlslapもインストールされているはずなので、すぐ使えます

基本的には、測定する際特定のスキーマを作って、テキトーなクエリを実行してベンチマークを取る感じのようですが、 今回は実際の本番相当のデータ量のテーブルを用意し、それに対して測定を行いました。

#測定するSQL
SELECT * FROM TMP_TABLE WHERE hoge = 'hoge'
/usr/local/mysql/bin/mysqlslap\
 --no-defaults\
 --user=USER\
 --password=PASS\
 --host=localhost\
 --port=3306\
 --engine=innodb\
 --concurrency=500\
 --iterations=30\
 --create-schema=TMP_DB\
 --query="SELECT * FROM TMP_TABLE WHERE hoge = 'hoge'"


Benchmark
        Running for engine innodb
        Average number of seconds to run all queries: 0.755 seconds
        Minimum number of seconds to run all queries: 0.702 seconds
        Maximum number of seconds to run all queries: 0.795 seconds
        Number of clients running queries: 500
        Average number of queries per client: 1

的な感じでベンチマーク取れます! 500回SELECTするのを30回行い、それらの最大最小平均が取れます

オプション
オプション 内容
no-defaults 設定ファイルに書かれたデフォルトをスキップ
user 接続ユーザ
password パスワード
host mysqlのhost
port mysqlのpoot
engine 対象ストレージエンジン
concurrency シミュレートする実行の数
iterations 実行するテストの回数
create-schema テストを実行するスキーマ
query 実行するクエリ(ファイル指定も可能)

今回は作成済みのDB、テーブルに対して実行したのですが、 他にもテキトーなクエリを自動生成して、ベンチマークを取ることもできるようです。 というかそのほうが本来の使い方っぽいですね。。。

設定ファイルいじって性能確認する的な。



ともあれとってもお手軽!

ベンチマークというと手間かかるなーと思いがちですが、 これなら、実装後にさくっと確認できていい感じです!

もっと早く知りたかった・・・!




感謝致します。

f:id:ogataka50:20150708173637j:plain

jenkinsの表示が真っ白になった、そんな時の解決法

ある日、jenkinsにアクセスしたら、表示が真っ白になりました・・・

テキストは表示されるのですが、css,imageが404になっておりレイアウトが崩れてました。

事象

  • jenkinsの表示で、cssやimageが404になり、真っ白の表示になる

原因

  • jenkinsのuiのファイルが、/tmp以下に作られるのでサーバーの設定によってファイルが削除されるため

対応方法

  • 【取り急ぎ系】jenkins再起動すればuiファイルは作りなおされます
  • 【根本解決】Javaのオプションでtmpファイルを作る場所を別に指定する必要があるようです


参考にさせて頂きましたm(_ _)msnickerjp.blogspot.jp


普通にJENKINS_HOMEの下に作ってくれればいいのに・・・



感謝致します。

f:id:ogataka50:20150513180812j:plain

理論から学ぶデータベース実践入門を読んだメモ

読んだので気になった点をいくつかメモ。

全体としては、
RDBのもととなっているリレーショナルモデルの解説と、
それを軸にRDBでよく起きる問題に対する解説がされているようなイメージでした。

タイトル通り理論が中心で読み物っぽい印象。

肝心のリレーショナルモデルは書いてあることの意味はわかるんですが、
まだまだ実感をもってはら落ちするまでできなかったので、
いつかRDBで行き詰まった時に読み直してみたいです。

以下、気になった箇所メモ。

インデックス

  • インデックスは左端から順にソートされる
  • 左端の文字からindexを作成していく
    • WHERE LIKE "a%" => index効く
    • WHERE LIKE "%a%" => index効かない
  • 複合インデックスの場合も左端から順にソートされるので、カラムの並び順が重要

パーティショニング

  • 範囲やキーでパーティショニングを分割
  • パーティショニングが適しているケース
    • キーのカーディナリティが低い場合
    • それ以外の場合はインデックスで十分な場合が多い
    • 最新のデータへのアクセスが多いアプリの場合、日付でパーティショニングするのは有効。パーティショニングごとにindexが作成されるので
  • インデックスはクエリが決まってから検討する
  • 下記の場合は、インデックスを貼らずにテーブルスキャンをしたほうがよいケースがある
    • 実行頻度が低い
    • テーブルのサイズが小さい
    • 検索結果が非常に多くの行にヒットする

インデックスの付け方

  • where句から

SELECT * FROM t WHERE col1 > 100 AND col2 = 'abc';
(col1, col2)の複合インデックスより、(col2, col1)のほうが効率がいい
col1 > 100 は膨大な数ヒットする可能性がある

  • join,サブクエリでもインデックスは有効なので、joinする条件サブクエリのWHERE句からインデックスを検討する

ソート

  • WHERE col1 = 100 AND col2 = 200 ORDER BY col3

という条件なら、インデックス(col1, col2, col3)でインデックスだけで解決できる
インデックスはcol1 -> col2 -> col3の順でソートされ作成されているので、
WHERE col1 = 100 ORDER BY col3
になると遅くなる

ORに対するインデックス

  • WHERE col1 = 100 OR col2 = 200

の時は(col1),(col2)の2つのインデックスが必要。インデックスマージされ検索される

インデックスはカラムの並び順が重要

  • 同じカラムの組み合わせでも並び順が異なるインデックスを作る時がよい時もある

カーディナリティ

  • WHERE句でcol1,col2,col3が条件となっていても、col1,col2でカーディナリティが高ければ、(col1, col2)だけのインデックスで十分なケースもある。
    • インデックスが増えれば、更新時のオーバーヘッドが発生するため。

インデックスは

  • どのカラムを含めるべきか、カラムのカーディナリティ、テーブルのサイズ、更新時のオーバーヘッド、クエリの実行頻度から総合して考える


やはりインデックスとかすぐに実践できる系のやつが気になっちゃます><


感謝致します。

f:id:ogataka50:20150506143458j:plain

PHPerが始めるgo言語入門 〜言語仕様〜

続いてgo言語の言語仕様を確認していきます。
五月雨でアレですが。

コメントアウト

1行
//

複数行
/*

*/

行末のセミコロン

ステートメント最後のセミコロンは省力可能。コンパイラが適切に挿入してくれる

インポート

import (
	"fmt"
	"strings"
)

import (
    f "fmt" //fという別名を使う
    _ "os" // _は対象パッケージを使わないことをコンパイラに明示する。使っていないパッケージがあるとコンパイルエラーになる
    . "strings" // .でパッケージ名を省略できる
)

変数の宣言

var 変数名 型 = 値
var message string = "hellow world"

複数同時に宣言

var hoge, foo, bar string = "hoge", "foo", "bar"

var (
	a string = "a"
	b		 = "b"
	c		 = "c"
)
同じ型なら2つ目以降は省力可能

関数内部での変数宣言

:=で型推論で宣言できる

func main() {
	// 同じ意味
	// var message string = "hello world"
	message := "hello world"
	fmt.Println(message)
}

定数

constで定数に
const Message string = "hello world"

初期値なしで宣言

var i int // i = 0
int => 0
float => 0.0
bool => false
string => ""
配列 => 要素0の配列
構造体 => プロパティが0の構造体
その他 nil

if文

if hoge > foo {
	
} else if hoge < foo {
	
} else {
	
}

3項演算子はなし
if1行だけもなし

ループ

ループはforだけ

for i := 0; i < 10; i++ {
	
}

いわゆるwhile的なfor
i := 0
for i < 10 {
	fmt.Printf("i = %d\n", i)
	i++
}

break,continue

i := 0
for {
	i++
	if i > 10 {
		break //ループ抜ける
	}

	if i % 2 == 0 {
		continue //偶数なら次のループへ
	}
	fmt.Println(n)
}

switch文

switch i {
	case 1:
		fmt.Println("1")
	case 2, 4:
		fmt.Println("2 or 4")
	case 5 < i:
		fmt.Println("greater 5")
	default
		fmt.Println("default")
}

switchで比較もオッケー
phpだとcaseに入ってもbreakを書かないと次のcaseへ進むが、golangは1つcaseが実行されたら、switchを抜ける
逆に1つcaseを実行されても次のcaseに行きたい時は[fallthrough]と書く

switch i {
	case 1:
		fmt.Println("1")
		fallthrough
	case 2, 4:
		fmt.Println("2 or 4")
		fallthrough
	default
		fmt.Println("default")
}

関数

func 関数名(引数 型) 戻り値の型 {
	
}


func sum(i, j int) { //sum(i int, j int)
	fmt.Println(i + j)
}


func sum(i, j int) int { //sum(i int, j int)
	return i + j
}
func main() {
	n := sum(1, 2)
	fmt.Println(n) //3
}


func change(i, j int) (int, int) {
	return j, i
}
func main() {
	x, y := 3, 4
	x, y = change(x, y)
	fmt.Println(x, y) //4 3
}

名前付きの戻り値

func div(i, j int) (result int, err error) {
	if j == 0 {
		err = errors.New("divied by zero")
		return // return 0, errと同じ
	}
		result = i / j
		return // return result, nilと同じ
}

無名関数

func main() {
	func(i, j int) {
		fmt.Println(i + j)
	}(2, 4)
}

配列

goの配列は固定長
var arr1 [4]string //4つの要素がある配列
arr[0] = "a"
arr[1] = "b"
arr[2] = "c"
arr[3] = "d"
fmt.Println(arr[0]) // a

// 宣言と同時に初期化
// どちらも同じ
arr := [4]string{"a", "b", "c", "d"}
arr := [...]string{"a", "b", "c", "d"}

関数に配列渡す時は値渡し

スライス

可変長の配列
var s []string

s := []string{"a", "b", "c", "d"}
fmt.Println(s[0]) // "a"

値追加
var s []string
s = append(s, "a") // 追加した結果を返す
s = append(s, "b")
s = append(s, "c", "d")
fmt.Println(s) // [a b c d]

s1 := []string{"a", "b"}
s2 := []string{"c", "d"}
s1 = append(s1, s2...) // s1にs2を追加
fmt.Println(s1) // [a b c d]

range

先頭から順番に処理
いわゆるforeach的な感じ

arr := [...]string{"a", "b", "c", "d"}

for i, s := range arr {
	// i = 添字, s = 値
	fmt.Println(i, s)
}

値の切り出し

s := []int{0, 1, 2, 3, 4, 5}
fmt.Println(s[2:4]) // [2 3]
fmt.Println(s[0:len(s)]) // [0 1 2 3 4 5]
fmt.Println(s[:3]) // [0 1 2 3]
fmt.Println(s[3:]) // [3 4 5]
fmt.Println(s[:]) // [0 1 2 3 4 5]

可変長引数

phpでもこういうのできるようになりますね
可変長で引数を取ることができる
func sum(nums ...int) (result int) {
	// numsは[]int型
	for _, n := range nums { //添字は捨てる
		result += n
	}
	return
}

func main() {
	fmt.Println(sum(1, 2, 3, 4)) // 10
}

マップ

ハッシュ的なやつです
キーと値で、格納

var month map[int]string = map[int]string{}

month[1] = "January"
month[2] = "February"
fmt.Println(month) // map[1:January 2:February]

month := map[int]string{
	1: "January",
	2: "February",
}
fmt.Println(month) // map[1:January 2:February]


値の取得
jan := month[1]
fmt.Println(jan) // January

2つ目の戻り値に値があるかboolで返してくれる
_, ok := month[1]
if ok {
	// データがあった場合
}

マップから情報を消すのはdelete

delete(month, 1)
fmt.Println(month) // map[2:February]

かなり雑でアレですが。。。



感謝致します。
f:id:ogataka50:20150421003559j:plain

PHPerが始めるgo言語入門 〜インストール〜 

これまでperlちょぴり、ほぼphpでお仕事をしてきたのですが、
このままだともろもろ幅が広がらない危機感を感じていました。

とはいえ、C,javaとかやるのもなー
rubyやってもなー
な感じで行動に移していなかったのですが、
最近go言語が広まりつつあり、
静的型付け,コンパイルで処理が高速,並列処理が容易とのことで、
phpとは違う知識がつけれそう+おいおい仕事にもつながる可能性もありそうなので、
ちょっと本腰入れて取り組んでみようと思います。


なので、何はともあれインストールから。

Go言語のインストール - golang.jp
こちらを参考にインストール

  • ソースをDL、解凍
  • PATH設定
  • インストール確認

ソースはこちらから
Downloads - The Go Programming Language

cd /usr/local
wget https://storage.googleapis.com/golang/go1.4.2.linux-amd64.tar.gz
tar -xzf go1.4.2.linux-amd64.tar.gz

vi /etc/profile
    export PATH=$PATH:/usr/local/go/bin
go version
go version go1.4.2 linux/ad64

でインストール完了!

続いてhello world

hello.goを作成します。

vi hello.go

package main

import "fmt"

func main() {
    fmt.Printf("hello, world\n")
}

実行してみます
go runでコンパイルせずに実行できるそうです。

go run hello.go

>hello, world

問題なさそうです。

今回は一旦ここまで。
次回からは、

  • 言語仕様の確認
  • 並列処理してみる
  • WEBアプリつくってみる

とかとかやっていければと思います!



以上となります。

感謝致します。
f:id:ogataka50:20150419221410j:plain