個人的コンピューター素人がインフラ屋になって1年目に読んで良かった本達

インフラ屋(主にDBA)として読んで良かったなぁこれ、って書籍です。 ドキュメントが最も読むべきものですが、基礎をしっかりという意味でやはり書籍は避けられないと思っている。 HOW TOとか流行りモノではなくて、昔から読まれている古い本を中心に今後も取り組みたい。 「タダより高いものはない」精神で、本はどんどん買って失敗したなーってのは数知れず。


徹底図解 パソコンが動くしくみ

徹底図解 パソコンが動くしくみ

徹底図解 通信のしくみ 改訂版

徹底図解 通信のしくみ 改訂版

CPUとか見たことねぇしH/W全く知らない、って時に中古安いやんって買ったら 当時の自分にはすごく良かった。ついでに通信の仕組み買ったけどこれはちょっと話が幅広すぎたけど まぁネットワークのイメージにもなるしいいと思います。


それがぼくには楽しかったから 全世界を巻き込んだリナックス革命の真実 (小プロ・ブックス)

それがぼくには楽しかったから 全世界を巻き込んだリナックス革命の真実 (小プロ・ブックス)

トーバルズの話。得体も知れないLinuxを触り始めて、トーバルズってどんな人やったんやろって読んだ。 (あんまり話覚えてないけど)今触ってるこのOSってこういう風に生まれたんだなーって読めます。


UNIXという考え方―その設計思想と哲学

UNIXという考え方―その設計思想と哲学

ページ数的には薄いが内容はものすごく為になる必読書。


言語の基本は色々触って勉強してたけど、これはLinuxの仕組みって書いてあるだけあってやってみて良かった。私が読んだのは第二版ではないです。個人的にはストリームの概念を知れたのが良かったという思い出。


How 〜 worksシリーズはだいたい目を通した。タイトル通りで素人でも読めるように書いてあって良かった。


絵で見てわかるITインフラの仕組み (DB SELECTION)

絵で見てわかるITインフラの仕組み (DB SELECTION)

周りのインフラエンジニアでこれを勧める人がちらほらいた。


RDBMS解剖学 よくわかるリレーショナルデータベースの仕組み (DB Magazine Selection)

RDBMS解剖学 よくわかるリレーショナルデータベースの仕組み (DB Magazine Selection)

DBの基本について。製品によらずに語っているところが好き。


How To Become A Hacker: Japanese

書籍ではないですが、一読して損なしと思っています。 こんな風になりたい。

MySQLのmysqlクライアントについての小ネタ的な

小ネタ過ぎてちょいちょい忘れてしまうので備忘のため書きます。

  • --auto-vertical-output

    • 「結果セットが現在のウィンドウに対して大きすぎる場合には縦に表示され、そうでない場合は通常の表形式が使用されるようになります。(これは、; または \G で終了するステートメントに適用されます。)」

    • --vertilcal,-Eでは常にverticalのに対してこいつは結果がワイド過ぎると縦表示してくれる。お気に入りなんだけどシノニムがない悲劇。

  • --html,-H、--xml,-X

    • 文字通りその形式で出力。mysql -H -e "CALL sys.diagnostics(120,30,'current');"みたいにやればそれっぽいレポートに?
  • --skip-column-names,-N

    • カラム名を出力しない。整形用に出力したいときよく使う。
  • --silent,-s

    • 表形式の結果を抑える。整形用に出力したいとき。
  • --verbose,-v

    • 冗長モード。ログインする時とかなんか詳しく出ている。個人的にはこれつけると以下のように実行ステートメントが出力されるので、mysql -v -e "SELECT host, user FROM mysql.user¥G" >> output.txtみたいにやると実行ステートメントも記録するので、そうしたい時用。
--------------
SELECT USER()
--------------

+----------------+
| USER()         |
+----------------+
| root@localhost |
+----------------+
1 row in set (0.00 sec)
mysql> help me

Nothing found
Please try to run 'help contents' for a list of all accessible topics
  • MYSQL_PWD

    • UNIXでの環境変数で、パスワードを仕込む。Warning: Using a password on the command line interface can be insecure.ってなるのが都合悪いときよく使う。セキュリティ的にパスワードは.mylogin.cnfmysql_config_editor使って書くものじゃないのかと思ってるのですがどうなんでしょうか。けどまぁそういうことやってないとこは多数なので、お願いする時はこう書いたりする。
  • mysqldコマンドについても一つ

    • mysqld --initialize-insecure --user=mysqlで初期化して、mysqladmin password passwordでやると検証環境は楽ちん。MySQL5.7系でvalidationがデフォルトのバージョンだとポリシーの従うパスワードを入れなきゃいけないけど、検証環境なのでそういうめんどくささを避ける。

innodb_read_onlyとread_onlyの違い

Auroraを触っていてMariaDB connector Jがinnodb_read_onlyを使ってWriterの判別してる。 って、innodb_read_onlyってパラメータ。read_only、super_read_onlyと何が違うんや。。。

innodb_read_only

MySQL :: MySQL 5.6 リファレンスマニュアル :: 14.3.1 読み取り専用操作用の InnoDB の構成

読んでみると、変更バッファとかredoとかundoとかその辺の書き込み系で必要になる系の動作がなくなる系。

MySQL :: MySQL 5.6 リファレンスマニュアル :: 14.12 InnoDB の起動オプションおよびシステム変数

また、こいつは静的変数。 だからAuroraはフェールオーバー時にWriterもレプリカも再起動してんのかな・・・? とりあえずread_onlyよりもこっちを使うメリットがあるからAuroraはinnodb_read_onlyなんだよなーきっと、と思っている。

read_only

MySQL :: MySQL 5.6 リファレンスマニュアル :: 5.1.4 サーバーシステム変数

こっちは動的変数。加えて説明はレプリケーション辺りの説明になっている。

よって

innodb_read_onlyは用途が読み取り専用メディアで起動するときに writeで必要なる機能あたりを動作させないとかの目的であるように読めます元来は。 静的変数なので、rootであろうとサーバ再起動しないと読み取り専用を変更できない。

read_onlyは動的なので起動中に変更できる。レプリケーションに使ってるよね。

という違いかと思ったのですがどうでしょうか・・・?

sysスキーマについて整理。PROCEDURE(その1)

手元の環境MySQL5.7.20でsysスキーマについて整理。 個人的にはMySQLのドキュメントよりgithubのREADMEの方がちゃんと書いてあるように思える。(確かめてないけど)

mysql> SELECT * FROM sys.version;
+-------------+---------------+
| sys_version | mysql_version |
+-------------+---------------+
| 1.5.1       | 5.7.20        |
+-------------+---------------+
1 row in set (0.00 sec)

PROCEDURE達

mysql-sys/procedures at master · mysql/mysql-sys · GitHub

ps設定確認系

sys.ps_setup_show_disabled
sys.ps_setup_show_enabled
  • 一つ目のテーブルでperformance_schemaが有効か表示してくれる。
    • SELECT @@performance_schema AS performance_schema_enabled;、を出してる
  • 二つ目のテーブルで有効なuserを表示してくれる。
    • SELECT * FROM setup_actors;、でuserとhostをくっ付けて出してる。MySQLのユーザー仕様。
  • 三つ目のテーブルは有効なobjects
    • performance_schema.setup_objectsから。
  • 4つ目、5つ目のテーブルはパラメータ次第。
    • パラメーターは二つとる。
      • 1つめ:instrumentsを表示するか否か。BOOLEAN値。
      • 2つめ: 動作中のスレッド。BOOLEAN値。
sys.ps_setup_show_disabled_consumers
sys.ps_setup_show_disabled_instruments
sys.ps_setup_show_enabled_consumers
sys.ps_setup_show_enabled_instruments
  • これらは普通にCALLしてあげればよい。

disabledenabledを両方用意してくれているのですが、必要なのかはまだわからない…

とりあえずCALL sys.ps_setup_show_enabled(TRUE,TRUE);と打つように自分は統一しよう。。。

instruments、consumer、background_thread、threadの設定

ps_setup_disable_background_threads
ps_setup_disable_consumer
ps_setup_disable_instrument
ps_setup_disable_thread

ps_setup_enable_background_threads
ps_setup_enable_consumer
ps_setup_enable_instrument
ps_setup_enable_thread

その名の通り。UPDATEで変えている。 background_threadとかthreadをdisableする時ってどんな時なんでしょう。。。

setup_xxx系をごにょごにょする

performance_schema.setup_xxxというテーブルはMySQLの再起動と共に上述のPROCEDUREとかで変更した設定は失われます。 なので、my.cnfに以下のパラメータを書くことで起動時に有効化してあげます。

MySQL :: MySQL 5.6 Reference Manual :: 22.12 Performance Schema Option and Variable Reference

# instrument
performance-schema-instrument=instrument_name=value

# consumer
# xxxにsetup_consumerと同じ名前が入るはず。
performance-schema-consumer-xxx

また、PROCEDUREで以下のようなものがあるので保存したり、デフォルトに戻したいときはこれを使えばラクチンっぽい

ps_setup_save
ps_setup_reload_saved

ps_setup_reset_to_default
ps_setup_reset_to_default_57
ps_setup_reset_to_default_57_after
ps_setup_reset_to_default_57_before

その他

ps_truncate_all_tables

文字通りperforrmance_schemaのテーブルをtruncateしてくれる。 性能検証でよく使ってたりする。

create_synonym_db
execute_prepared_stmt
table_exists

文字通り。

便利なやつ

以下、TO BE CONTINUEとします。 しっかり使い方を知っておきたいところ。 それぞれ一つずつ整理できるレベルと思うので別ポストにしよう・・・

diagnostics
ps_statement_avg_latency_histogram
statement_performance_analyzer
ps_trace_statement_digest
ps_trace_thread
ps_trace_thread_57

まとめてみて

FUNCTION,PROCEDURE,VIEWとかもgithub見たりしてるほうが見やすい。 あーこうやって書いてあるんだーとか見れておもしろい。

Oracle DatabaseみたいにStatspackとかないんですか?って言ってきたら こういうのがあるよみたいなのもいつかMySQLにもできるのかなぁとdiagnosticsとか見てると思う。

またperformance_schema及びsysスキーマについてはMySQL Workbenchが使いやすいのでそちらも要参考でまとめてみたい。。。

MySQL :: MySQL Workbench Manual :: 7.2 Performance Schema Reports

MySQLのステータス変数とかをcsvで直接取得しようと思ったときのメモ。

nyamada2.hatenablog.com

という風に頑張って整形スクリプトを作成しましたがもっとお手軽に整形されたデータを取得する方法を思いついた。 このやり方に課題がなければこれでやるのもありかと考えている。

information_schemaからINTO OUTFILE句を使って書く方法。

SELECT 'Variable_name','Value'
UNION
SELECT * FROM information_schema.global_status
INTO OUTFILE '/var/lib/mysql/global_status.csv'
FIELDS TERMINATED BY ',';

と思ったら"ERROR 1086 (HY000): File '/var/lib/mysql-files/global_status.csv' already exists" そう。追記できない。。。

けれども「mysql csv 出力」とかぐぐってたら次に思いついたのが以下。

mysql --silent -u user -p password \
 -e "SELECT VARIABLE_VALUE FROM information_schema.global_status;" |\
 sed -e 's/\t/,/g' >> OUTPUT_FILE

しかしこれでは追記出来ても結果が縦に連続して追記されてしまうではないか・・・ そうだ。

array=( `mysql --silent -u user -p password \
-e "SELECT SYSDATE(); SELECT VARIABLE_VALUE FROM information_schema.global_status;"` ) \
&& echo ${array[@]} | sed 's/ /,/g' >> global_status.csv

341列というとてつもなく見にくいcsvファイルが出来た! ループさせる場合は毎回配列の中身を消すとかすればいける!

(普通に生データ取って整形すればいいか・・・)

実践ハイパフォーマンスMySQLを参考に情報収集スクリプトを書いてみました

実践ハイパフォーマンスMySQLを参考にMySQLのメトリック取得のスクリプトを作成しました。 シェルスクリプトの勉強のため、余計な機能をいくつかつけました。

日に日に継ぎ足ししているため、すごく長くなってきました。。。

As I read High Perfomance MySQL, there is a exmple script getting mysql status metrics. Also for my study shellscript, I created the script and added as much useful functionality as I could.

取得スクリプト

getoptsを使ってみたかったため、なんか長いです。

引っかかったところが、getoptsでフラグを立てて、フラグの判定で その処理を実行したかったのですが、オプションがないときにFLAGの変数に値がないと [ ]内の判定で以下のエラーが発生し、回避策が のように二重で囲むと良いらしいです。

[: !==: 単項演算子が予期されます

参考 https://bm-server.net/2015/08/07/379/

#!/bin/bash

##
INTERVAL=3
BASE_OUTPUT_DIR=/tmp/mysql_metric_data
##

SCRIPT_PID=$$
SCRIPT_NAME=`basename $0`

function usage() {
cat <<_EOT_
Usage:
  ${SCRIPT_NAME} -user -password [ -host -time -report-time -zip ] &

Description
 -u : MySQL user
 -p : MySQL password
 -h : MySQL Server Hostname
 -t : Script loop time
 -r : Repoting elapsed time while script executing, but it cannot set under ${INTERVAL} time.
 -z : zip results file after script completed.

 * This scprit should be executed as background job!

_EOT_
}


while getopts u:p:h:t:r:z OPT
do
  case $OPT in
   u ) MYSQL_USER="$OPTARG"
        ;;
   p ) MYSQL_PWD="$OPTARG"
        ;;
    h ) FLAG_H="TRUE" ; MYSQL_HOST="$OPTARG"
        ;;
    t ) FLAG_T="TRUE" ; SAMPLING_LOOP_TIME="$OPTARG"
        ;;
    r ) FLAG_R="TRUE" ; REPORT_TIME="$OPTARG"
        ;;
    z ) FLAG_Z="TRUE"
        ;;
   \? ) echo "[NOTICE] ${SCRIPT_NAME} is not correctly excuted."
     usage
     exit 1
  esac
done

if [[ ${FLAG_H} != 'TRUE' ]] ; then
 echo "[NOTICE] When -h option value is not given, it set 'localhost'"
 MYSQL_HOST="localhost"
fi

if [[ ${FLAG_T} != 'TRUE' ]] ; then
 echo "[NOTICE] When -t option value is not given, it set '86400'"
 SAMPLING_LOOP_TIME="86400"
fi

for (( i=1; i <= $#; i++  ))
do
 eval echo "[NOTICE] argv[$i] = \$$i"
done

export MYSQL_PWD
MYSQL_EXE_CMD="mysql -u ${MYSQL_USER} -h ${MYSQL_HOST}"
MYSQL_ADMIN_CMD="mysqladmin -u${MYSQL_USER} -h${MYSQL_HOST}"
SAMPLING_REPEAT_COUNT=`expr $SAMPLING_LOOP_TIME / $INTERVAL`
SAMPLING_COUNTER=0 # Initiaize
SCRIPT_OUTPUT_LOWEST_DIR_NAME=`date "+%Y%m%d-%H%M%S"`
SCRIPT_OUTPUT_EACH_DIR=${BASE_OUTPUT_DIR}/${SCRIPT_OUTPUT_LOWEST_DIR_NAME}
RUNFILE=/var/run/${SCRIPT_NAME}-${SCRIPT_PID}.pid


if  [ ! -e ${SCRIPT_OUTPUT_EACH_DIR} ]; then
 mkdir -p ${SCRIPT_OUTPUT_EACH_DIR}
 echo "[NOTICE] Created ${SCRIPT_OUTPUT_EACH_DIR}. Result data will write here."
fi

$MYSQL_ADMIN_CMD ping 1>/dev/null 2>/dev/null
if [ $? -eq 1 ]; then
  echo "[ERROR] Cannot connect to ${MYSQL_HOST}."
  echo "[ERROR] Script aborted."
  exit 1
else
  echo "[NOTICE] Connecting to ${MYSQL_HOST} successed."
fi

# Below executing only one time
$MYSQL_EXE_CMD -e 'SHOW DATABASES\G' >> ${SCRIPT_OUTPUT_EACH_DIR}/SHOW_DATABASE.txt
$MYSQL_EXE_CMD -e 'SELECT * FROM mysql.user\G' >> ${SCRIPT_OUTPUT_EACH_DIR}/mysql.user.txt
$MYSQL_EXE_CMD -e 'SHOW GLOBAL VARIABLES\G' >> ${SCRIPT_OUTPUT_EACH_DIR}/SHOW_GLOBAL_VARIABLES.txt
 

touch ${RUNFILE}
echo "[NOTICE] ${RUNFILE} is created. Loop to get metrics will start."
echo "[NOTICE] When you want to stop process ${SCRIPT_PID}, delete ${RUNFILE} file."
echo "[NOTICE] DO NOT try to kill process directly."

while [ -e ${RUNFILE} ];
do
 file=$(date +%F_%I)
 sleep=$(date +%s.%N | awk "{print $INTERVAL - (\$1 % $INTERVAL)}")
 sleep $sleep
 ts="$(date +"TS %s.%N %F %T")"

 echo "$ts"            >> ${SCRIPT_OUTPUT_EACH_DIR}/TIMESTAMP.txt
 echo "$ts"            >> ${SCRIPT_OUTPUT_EACH_DIR}/SHOW_GLOBAL_STATUS.txt
 $MYSQL_EXE_CMD -e 'SHOW GLOBAL STATUS'     >> ${SCRIPT_OUTPUT_EACH_DIR}/SHOW_GLOBAL_STATUS.txt &
 echo "$ts"            >> ${SCRIPT_OUTPUT_EACH_DIR}/innodbstatus
 $MYSQL_EXE_CMD -e 'SHOW ENGINE INNODB STATUS\G'   >> ${SCRIPT_OUTPUT_EACH_DIR}/innodbstatus &
 echo "$ts"            >> ${SCRIPT_OUTPUT_EACH_DIR}/processlist
 $MYSQL_EXE_CMD -e 'SHOW FULL PROCESSLIST\G'    >> ${SCRIPT_OUTPUT_EACH_DIR}/processlist &

 if [ ${SAMPLING_COUNTER} -eq 0 ]; then
  $MYSQL_ADMIN_CMD -r -i $INTERVAL ext     >> ${SCRIPT_OUTPUT_EACH_DIR}/MYSQL_ADMIN_extended-status.txt &
  MYSQL_ADMIN_CMD_INTERVAL_PID=`pgrep mysqladmin`
  echo "[NOTICE] mysqladmin -r -i $INTERVAL ext (${MYSQL_ADMIN_CMD_INTERVAL_PID} pid) process started as backgourd job."
 fi

 SAMPLING_COUNTER=`expr ${SAMPLING_COUNTER} + 1`
 if [ ${SAMPLING_COUNTER} -gt ${SAMPLING_REPEAT_COUNT} ]; then
  echo "[NOTICE] ${SAMPLING_LOOP_TIME} secouds passed."
 
  if [[ ${FLAG_Z} = 'TRUE' ]] ; then
   echo "[NOTICE] Result files is going to be zipped because -z option is set."
   zip -r ${BASE_OUTPUT_DIR}/${SCRIPT_OUTPUT_LOWEST_DIR_NAME}.zip ${SCRIPT_OUTPUT_EACH_DIR} 1>/dev/null 2>/dev/null
   ZIP_FLAG=`echo $?`

   if [[ ${ZIP_FLAG} -eq 1 ]]; then
    echo "[ERROR] zip failed."
   else
    echo "[NOTICE] zip succeded."
   fi
  fi

  rm -f ${RUNFILE}
  echo "[NOTICE] ${RUNFILE} removed by script."
  kill -9 ${MYSQL_ADMIN_CMD_INTERVAL_PID}
  echo "[NOTICE] ${MYSQL_ADMIN_CMD_INTERVAL_PID} backgrouod process killed."
  echo "[NOTICE] ${SCRIPT_NAME} is going to end..."
 
 exit 0
 fi

 if [[ ${FLAG_R} = 'TRUE' ]] ; then
  if [ `expr ${INTERVAL} \* ${SAMPLING_COUNTER} % ${REPORT_TIME}` -eq 0 ]; then
  echo "[NOTICE] `expr ${INTERVAL} \* ${SAMPLING_COUNTER}` seconds passed. Scripts keeps on working!"
  fi
 fi

done

echo ""
echo "[NOTICE] Script Completed !"
echo "[NOTICE] Existing because $RUNFILE does not exist."
kill -9 ${MYSQL_ADMIN_CMD_INTERVAL_PID}
echo "[NOTICE] mysqladmin -r -i $INTERVAL ext (${MYSQL_ADMIN_CMD_INTERVAL_PID} pid) backgrouod process killed."
exit 0

#EOF

整形スクリプト

上のスクリプトで収集した結果でSHOW GLOBAL STATUSの結果を整形するスクリプトです。 整形したいメトリックを簡単に変更できるように別ファイルに記載して それを読み込んで実行するようにしました。

Here analyzing scrpit from above script result. Metrics that is analyzed possibly vary and I want to separate what metrics analyze. So script reads from external file.

こんな感じで、現在の値として整形したい場合はメトリックの頭に"-"の記号を付けておくことが必要です。

Com_insert
-Connections
#!/bin/bash
OUTPUT_DIR=/tmp/mysql_metric_data/analyze
CMDNAME=`basename $0`

function usage() {
cat <<_EOT_
Usage:
  ${CMDNAME} -f -m

Description
  -f    : FULLPATH filename that contains raw data.
  -m    : FULLPATH filename that contains metric names.
          Marking "-" first letter of metric name tells that it culculate as current values.

# Metric file example
value1  # accumulative value.
-value2 # current value.

_EOT_
}


while getopts f:m: OPT
do
    case $OPT in
        f   ) RAWDATA_FILE="${OPTARG}"
                ;;
        m   ) METRICS_FILE="${OPTARG}"
                ;;
        \?) usage
                exit 1
                ;;
    esac
done

if [ $# -ne 4 ]; then
    echo "[ERROR] Scirpt is not collectly executed!"
    echo ""
    usage
    exit 1
fi

if [ ! -e ${OUTPUT_DIR} ]; then
    mkdir -p ${OUTPUT_DIR}
    echo [NOTICE] Created ${OUTPUT_DIR} because it does not exist.
fi

# yyyy-mm-dd HH:MM:SS
echo "LABEL" >> ${OUTPUT_DIR}/1_TIMESTAMP.csv
echo "DATE_TIME" >> ${OUTPUT_DIR}/1_TIMESTAMP.csv
awk '
/^TS/ {
  printf "%s %s\n", $3, $4
}' ${RAWDATA_FILE} >> ${OUTPUT_DIR}/1_TIMESTAMP.csv

# Elapsed time
ARRAY_UTC_TIMESTAMP=( `awk '/^TS/{ print $2 }' ${RAWDATA_FILE} | cut -d'.' -f1` )
COUNT_END_0=`expr ${#ARRAY_UTC_TIMESTAMP[*]} - 1`
echo "," >> ${OUTPUT_DIR}/2_Elapsed_Time.csv
echo ",Elapsed_Time" >> ${OUTPUT_DIR}/2_Elapsed_Time.csv

for array_key_num in `seq 0 1 ${COUNT_END_0}`
do

    if [[ FLAG -eq 0 ]]; then
        UTC_START=${ARRAY_UTC_TIMESTAMP[${array_key_num}]} 
        echo ,0
        FLAG=1
    else
        echo ,`expr ${ARRAY_UTC_TIMESTAMP[${array_key_num}]} - ${UTC_START}`
    fi 

done >> ${OUTPUT_DIR}/2_Elapsed_Time.csv

# Processing metrics from metric file
ARRAY_ALL_METRICS=( `cat ${METRICS_FILE} | sed -e 's/^-//g' -e '/^$/d' -e '/^#/d'` )
ARRAY_CURRENT_VALUE=( `awk '/^-.*/' ${METRICS_FILE} | sed -e 's/^-//g' -e '/^$/d' -e '/^#/d'` )
echo "[NOTICE] ${ARRAY_CURRENT_VALUE[@]}"
echo "[NOTICE] will be calculated as current value."

for METRICS in ${ARRAY_ALL_METRICS[@]}
do
    regexp="^${METRICS}$"

    if [[ ${ARRAY_CURRENT_VALUE[@]} =~ ${METRICS} ]] ; then
        awk -v METRICS=${METRICS} -v regexp="${regexp}" '
        $1 ~ regexp {
            if ( FLAG==0 ) {
                print ",Current value"
                FLAG++
                print ","METRICS
                printf ",%s\n" , $2
                }
            else {
                printf ",%s\n" , $2
                }
        }' ${RAWDATA_FILE} >> ${OUTPUT_DIR}/${METRICS}.csv
    else    
        awk -v METRICS=${METRICS} -v regexp="${regexp}" '
        $1 ~ regexp {
            if ( FLAG==0 ) {
                START_VALUE=$2
                FLAG++
                print ",Accumulative value"
                print ","METRICS
                printf ",%s\n" , START_VALUE
                }   
            else {
                ACCUMULATIVE_VALUE=$2-START_VALUE
                printf ",%s\n" , ACCUMULATIVE_VALUE
                }
        }' ${RAWDATA_FILE} >> ${OUTPUT_DIR}/${METRICS}.csv
    fi
done

# Query Cache hit ratio
if [[ ${ARRAY_ALL_METRICS[@]} =~ Qcache_hits ]] ; then
    if  [[ ${ARRAY_ALL_METRICS[@]} =~ Com_select ]] ; then

        echo "[NOTICE] Query Cache hit ratio is calculating..."

        ARRAY_Qcache_hits=( `awk '/^Qcache_hits/{ print $2 }' ${RAWDATA_FILE}` )
        ARRAY_Com_select=( `awk '/^Com_select/{ print $2 }' ${RAWDATA_FILE}` )
        COUNT_END_1=`expr ${#ARRAY_Qcache_hits[*]} - 1`
        
        echo "," >> ${OUTPUT_DIR}/_Query_cache_hit_ratio.csv
        echo ",Query_cache_hit_ratio" >> ${OUTPUT_DIR}/_Query_cache_hit_ratio.csv
        
        for array_key_num in `seq 0 1 ${COUNT_END_1}`
        do

            # To calculate after decimal point, use awk
            echo ${ARRAY_Qcache_hits[${array_key_num}]} ${ARRAY_Com_select[${array_key_num}]} |\
            awk '{ print ","$1 / ( $1 + $2 ) }'

        done >> ${OUTPUT_DIR}/_Query_cache_hit_ratio.csv

    fi
fi

# InnoDB buffer hit ratio
if [[ ${ARRAY_ALL_METRICS[@]} =~ Innodb_buffer_pool_read_requests ]] ; then
    if  [[ ${ARRAY_ALL_METRICS[@]} =~ Innodb_buffer_pool_reads ]] ; then

        echo "[NOTICE] InnoDB buffer hit ratio is calculating..."

        ARRAY_Innodb_buffer_pool_read_requests=( `awk '/^Innodb_buffer_pool_read_requests/{ print $2 }' ${RAWDATA_FILE}`)
        ARRAY_Innodb_buffer_pool_reads=( `awk '/^Innodb_buffer_pool_reads/{ print $2 }' ${RAWDATA_FILE}` )
        COUNT_END_2=`expr ${#ARRAY_Innodb_buffer_pool_read_requests[*]} - 1`

        echo "," >> ${OUTPUT_DIR}/_InnoDB_buffer_pool_hit_ratio.csv
        echo ",InnoDB_buffer_pool_hit_ratio" >> ${OUTPUT_DIR}/_InnoDB_buffer_pool_hit_ratio.csv

        for array_key_num in `seq 0 1 ${COUNT_END_2}`
        do

            # To calculate after decimal point, use awk
            echo ${ARRAY_Innodb_buffer_pool_read_requests[${array_key_num}]} ${ARRAY_Innodb_buffer_pool_reads[${array_key_num}]} |\
            awk '{ print ","$1 / ( $1 + $2 ) }'

        done >> ${OUTPUT_DIR}/_InnoDB_buffer_pool_hit_ratio.csv

    fi
fi

# Combining result files
ARRAY_FULLPATH=( `find ${OUTPUT_DIR} -maxdepth 1 -type f` )
for i in ${ARRAY_FULLPATH[@]}
do 
    echo $i; 
done | sort -t / -k 3,3 | xargs paste >> ${OUTPUT_DIR}/0_METRIC_ANALYZED.csv

#EOF

sysbenchの使い方メモと整形スクリプト

sysbenchのバージョン1系の使い方です。MySQLで使用する場合で記載しています。

基本

シンタックスは以下の通りです。

sysbench [option] [testname] [command]

[testname]には、luaスクリプトかsysbenchのCompiled-in testsを入れます。 手元の環境sysbench1.0.9では以下のluaスクリプトが用意されています。

/usr/share/sysbench/bulk_insert.lua
/usr/share/sysbench/oltp_common.lua
/usr/share/sysbench/oltp_delete.lua
/usr/share/sysbench/oltp_insert.lua
/usr/share/sysbench/oltp_point_select.lua
/usr/share/sysbench/oltp_read_only.lua
/usr/share/sysbench/oltp_read_write.lua
/usr/share/sysbench/oltp_update_index.lua
/usr/share/sysbench/oltp_update_non_index.lua
/usr/share/sysbench/oltp_write_only.lua
/usr/share/sysbench/select_random_points.lua
/usr/share/sysbench/select_random_ranges.lua

[command]では、prepareとrun,cleanupが基本的に使用可能と思われます。 prepareでは、指定したテーブル行数とテーブル数に従ってsysbenchがテストデータを作成します。 runでは、指定したオプションに従ってテストが実行されます。 cleanupでは、指定したテーブル行数とテーブル数に従ってsysbenchがテストデータを削除します。

また、ヘルプページには記載がありませんが"/usr/share/sysbench/oltp_common.lua"の中身を見ると prewarmというコマンドが用意されています。ただし、現在はMySQLInnoDBのみ対応のようです。 機能はキャッシュにデータを載せる用途で、指定したテーブル行数とテーブル数に従ってsysbenchがSELECTを行っていると思われます。(luaスクリプトをしっかり読んでないのですがたぶん・・・)

"/usr/share/sysbench/oltp_common.lua"の91行目から

-- Preload the dataset into the server cache. This command supports parallel
-- execution, i.e. will benefit from executing with --threads > 1 as long as
-- --tables > 1
--
-- PS. Currently, this command is only meaningful for MySQL/InnoDB benchmarks

使用例

使用例のメモです。

prepare

mysql_host=
mysql_user=
mysql_password=
test=/usr/share/sysbench/oltp_read_write.lua 


sysbench \
--db-driver=mysql \
--mysql-host=$mysql_host \
--mysql-user=$mysql_user \
--mysql-password=$mysql_password \
--table-size=100 \
--tables=3 \
$test \
prepare

結果

sysbench 1.0.9 (using bundled LuaJIT 2.1.0-beta2)

Creating table 'sbtest1'...
Inserting 100 records into 'sbtest1'
Creating a secondary index on 'sbtest1'...
Creating table 'sbtest2'...
Inserting 100 records into 'sbtest2'
Creating a secondary index on 'sbtest2'...
Creating table 'sbtest3'...
Inserting 100 records into 'sbtest3'
Creating a secondary index on 'sbtest3'...

prewarm

sysbench \
--db-driver=mysql \
--mysql-host=$mysql_host \
--mysql-user=$mysql_user \
--mysql-password=$mysql_password \
--table-size=100 \
--tables=3 \
$test \
prewarm

結果

sysbench 1.0.9 (using bundled LuaJIT 2.1.0-beta2)

Prewarming table sbtest1
Prewarming table sbtest2
Prewarming table sbtest3

run

sysbench \
--db-driver=mysql \
--mysql-host=$mysql_host \
--mysql-user=$mysql_user \
--mysql-password=$mysql_password \
--table-size=100 \
--tables=3 \
--db-ps-mode=disable \
--time=30 \
--report-interval=15 --histogram=on \
$test \
run

結果

sysbench 1.0.9 (using bundled LuaJIT 2.1.0-beta2)

Running the test with following options:
Number of threads: 1
Report intermediate results every 15 second(s)
Initializing random number generator from current time


Initializing worker threads...

Threads started!

[ 15s ] thds: 1 tps: 82.45 qps: 1649.95 (r/w/o: 1155.18/329.80/164.97) lat (ms,95%): 17.32 err/s: 0.00 reconn/s: 0.00
[ 30s ] thds: 1 tps: 77.47 qps: 1549.64 (r/w/o: 1084.63/310.07/154.94) lat (ms,95%): 19.65 err/s: 0.00 reconn/s: 0.00
Latency histogram (values are in milliseconds)
       value  ------------- distribution ------------- count
       7.842 |*                                        2
       7.985 |*                                        3
       8.130 |**                                       4
       8.277 |****                                     11
       8.428 |******                                   14
       8.581 |*******                                  16
       8.737 |**********                               25
       8.895 |***********                              28
       9.057 |*****************                        41
       9.222 |*****************                        42
       9.389 |**********************                   53
       9.560 |******************************           74
       9.734 |**************************************** 98
       9.910 |**************************************   94
      10.090 |************************************     89
      10.274 |**************************************   92
      10.460 |*************************************    90
      10.651 |********************************         79
      10.844 |**************************************** 97
      11.041 |*****************************            72
      11.242 |***************************              65
      11.446 |********************************         78
      11.654 |********************************         78
      11.866 |******************************           73
      12.081 |***************************              67
      12.301 |***************************              65
      12.524 |******************************           73
      12.752 |***************************              65
      12.984 |*********************                    52
      13.219 |**********************                   54
      13.460 |***********************                  56
      13.704 |*********************                    51
      13.953 |*******************                      47
      14.207 |***************                          36
      14.465 |***********                              26
      14.728 |*****************                        41
      14.995 |*************                            32
      15.268 |****************                         40
      15.545 |*********                                23
      15.828 |**************                           34
      16.115 |**********                               25
      16.408 |*********                                23
      16.706 |********                                 20
      17.010 |**********                               25
      17.319 |*********                                23
      17.633 |********                                 20
      17.954 |********                                 19
      18.280 |*******                                  17
      18.612 |********                                 20
      18.950 |*********                                22
      19.295 |******                                   14
      19.645 |******                                   14
      20.002 |****                                     9
      20.366 |****                                     9
      20.736 |**                                       6
      21.112 |****                                     9
      21.496 |**                                       6
      21.886 |**                                       4
      22.284 |**                                       6
      22.689 |**                                       4
      23.101 |*                                        2
      23.521 |*                                        2
      23.948 |                                         1
      24.384 |                                         1
      24.827 |                                         1
      25.278 |**                                       5
      25.737 |**                                       5
      26.205 |*                                        2
      27.165 |                                         1
      27.659 |                                         1
      28.162 |                                         1
      29.194 |                                         1
      31.945 |                                         1
      41.851 |                                         1
 
SQL statistics:
    queries performed:
        read:                            33600
        write:                           9600
        other:                           4800
        total:                           48000
    transactions:                        2400   (79.98 per sec.)
    queries:                             48000  (1599.55 per sec.)
    ignored errors:                      0      (0.00 per sec.)
    reconnects:                          0      (0.00 per sec.)

General statistics:
    total time:                          30.0067s
    total number of events:              2400

Latency (ms):
         min:                                  7.85
         avg:                                 12.50
         max:                                 42.12
         95th percentile:                     18.95
         sum:                              29989.57

Threads fairness:
    events (avg/stddev):           2400.0000/0.00
    execution time (avg/stddev):   29.9896/0.00

 整形スクリプト

Excelなどに貼ってグラフ化を想定して、csvっぽく整形するスクリプトを書きました。 awksedに使い慣れてない感半端ないですが一応動くは動くはずです。。。

file_name=$1
log_base=/root
log_file=`date '+%Y%m%d'`

grep "Number of threads" $file_name >> $log_file

awk '/^\[/ { print $0 }' $file_name | \
awk '{printf "%3ds,%s,%s,%s,%s,%s,%s,%s\n" , $2 , $5 , $7, $9, $11, $14, $16, $18}' | \
sed 's/\//,/g' | sed 's/)//g' >> $log_file

awk '/read:|write:|other:|total:|transactions|queries:|(ignored errors:)|reconnects:|(total time:)|(total number of events)|min:|avg:|max:|(95th percentile:)|sum:/ {print $0}' $file_name | \
sed 's/ //g' | \
sed 's/)/,/g' | \
sed 's/:/,/g' >> $log_file

結果

Number of threads: 1
 15s,1,74.18,1484.55,1039.40,296.72,148.43,23.95,0.00,0.00
 30s,1,84.74,1694.80,1186.36,338.96,169.48,17.32,0.00,0.00
read,33390
write,9540
other,4770
total,47700
transactions,2385,79.47persec. 
queries,47700,1589.40persec. 
ignorederrors,0,0.00persec. 
reconnects,0,0.00persec. 
totaltime,30.0095s
totalnumberofevents,2385
min,7.44
avg,12.58
max,41.06
95thpercentile,20.37
sum,29991.83