大量のレコードをDELETEしたならOPTIMIZE TABLEをしようというお話

そもそも大量にDELETEしない方が安心ですが…。
LIMITをつけて刻んだDELETEでも同様です。
DBのストレージ容量の肥大化を防ぐため、ログテーブルなどのもう利用しない古いレコードを大量に物理削除したいときとかってあります。
物理削除した場合は、

OPTIMIZE TABLE table_name;

を実行しないとディスク領域が解放されません。
ただし、実行した場合はテーブルロックがかかります。
サービスがリアルタイムで稼働中の場合は、実行タイミングに注意が必要です。

試しに実行してみます。
以下のような適当なサンプルテーブルを用意してみます。

CREATE TABLE people (
    id int(11) NOT NULL,
    name varchar(64) NOT NULL,
    height int(11) NOT NULL,
    weight int(11) NOT NULL,
    gender enum('male','female') NOT NULL,
    updated timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

ALTER TABLE people
    ADD PRIMARY KEY (id),
    ADD KEY height (height),
    ADD KEY weight (weight);

ALTER TABLE people
    MODIFY id int(11) NOT NULL AUTO_INCREMENT;

まず、10行入れます。

INSERT INTO people (name, height, weight, gender) VALUES
 ('suzuki', 170, 65, 'male'),
 ('ohtani', 190, 90, 'male'),
 ('sato', 150, 45, 'female'),
 ('nakamura', 180, 75, 'male'),
 ('tanaka', 160, 55, 'female'),
 ('inoue', 155, 50, 'female'),
 ('ishida', 160, 60, 'male'),
 ('matsumoto', 165, 60, 'female'),
 ('sasaki', 175, 70, 'male'),
 ('kato', 145, 40, 'female');

10000010行に増やします。
自分の環境では2分30秒ぐらいかかりました。

INSERT INTO people (name, height, weight, gender)
SELECT p1.name, p1.height, p1.weight, p1.gender
FROM people p1, people p2, people p3, people p4, people p5, people p6, people p7;

ここで容量を確認しておきます。

SELECT table_name,
       table_rows AS rows,
       floor((data_length+index_length) / 1024 / 1024) AS all_mb,
       floor(data_length / 1024 / 1024) AS data_mb,
       floor(index_length / 1024 / 1024) AS index_mb
FROM information_schema.tables
WHERE table_schema = database()
ORDER BY (data_length + index_length) DESC;
table_name rows all_mb data_mb index_mb
people 9739621 660 406 253

およそ660MBになっています。
DELETE文でおよそ900万行削除したいと思います。
時間かかるのでブレイクタイムにすることをオススメします。
実行したら自分の環境では12分でした。

DELETE FROM people WHERE id > 1000000;

消し終えたらもう一回、容量を確認します。

table_name rows all_mb data_mb index_mb
people 1191489 660 406 253

行数は変わったけど容量は変わってないんですよね…。
そこで最適化します。

OPTIMIZE TABLE people;

もしくは

ALTER TABLE people ENGINE INNODB;

14秒くらいかかりました。

table_name rows all_mb data_mb index_mb
people 997458 81 50 31

減りましたのでめでたしです。
しかし、DELETE文が遅いのとテーブルがロックされるのが嫌がられるのがわかりますね。
一時テーブルを作成する方法が取られるのもこちらの結果を考えると納得がいきます。

参考サイト

コミットメッセージにチケット番号がない場合に警告を出すGithook

オンラインコンサルタントの直井です(^^♪

前に記事を投稿してから早一日が経過しました。
よく、「ローマは一日にして成らず」と言われますが、GitHooksは違います。
「GitHooksは一日にして作成すべき」これは、ローマ帝国から受け継がれてきた名言です。
なぜ、一日でやらなくてはいけないのか?
それは、業務効率化のツールは、すぐに共有すべきであるからです。効率的なツールの作成は有用な時間を作ることができるのです。

そんな冗談はさておき、今回の記事は以下の3本建てでお送りします。
・「GitHooksを利用してコミットメッセージに、ブランチ名からチケット番号を自動的に追加する」
・「チェックアウトしたときに、ブランチ名をチェックする」
・「GitHooksを共有する方法」


そもそも、何を解決したいのか?

そもそも、今回の問題はなんでしょうか?

現状:チケットとGitを連携するには、コミットメッセージにチケット番号が必要だが、コミットメッセージにチケット番号を入れるのを結構忘れがち → チケットとの関連付けや終了が自動的にされない。

解決したいこと:コミットメッセージにチケット番号を入れたい

ざっとまとめるとこんな感じです。なので、今回の問題はいたってシンプルに「
コミットメッセージにチケット番号を入れたい 」という願いをかなえるだけです。


どうやって実現させるか

状況が整理され、問題が明確になったところで、今度は最も重要な部分….
どうやってこの問題を解決するかです。

毎朝出社時に「 コミットメッセージにチケット番号を! 」と力技オペレーション解決もアリです。しかし、これはスマートでない解決方法です。

今回天才的な発想で思いついた解決方法は以下の通りです。
①:ブランチ名の最初にチケット番号を入れる
②:ブランチ名から自動的にコミットメッセージにチケット番号を入力する
③:①、②のアクションをする際に、チケット番号らしき数字が見つからなかったら警告を出す。

修正や新規機能を開発するときにすること、それはブランチを切る!ということは?その行為自体はチケット番号と結びつきがあるのです。また、ブランチに番号が付くことで、たくさんあるリモートブランチから目的のブランチを探しやすくなります。


1. ブランチ作成時チケット番号が書いていなさそうな時にエラーを表示するGitHookを作成

GitHookでは、様々なGitアクションに対してフックを設定することができます。
ブランチを作成したというアクションに対するフックはないので、厳密ではないですが、チェックアウトしたときに発動する「post-checkout」を利用します。

#!/bin/sh

# PHPStorm勢からのアツい要望
git pull

ticketNumber=`git branch | grep "*" | awk '{print $2}' | sed -e "s/^\([0-9]*\).*/\1/g"`
if [ -n "$ticketNumber" ]; then
    exit 0
else
    # チケット番号がないブランチ
    echo -e "チケット番号がありません。"
    exit 1
 fi

PHPStorm勢からのアツい要望とありますが、チェックアウトしたときにGitPullされていないことが何回かあったというお話を聞いてついでに入れました。

それ以降は、ブランチ名からチケット番号らしき数字を取得して、それがあればOKなければエラーを表示するようにしました。

eixt のコード0が正常に終了したことを伝え、エラーの場合は0以外(ここでは1)を指定するようにします。


2. ブランチ名からチケット番号を取得して自動的にコミットメッセージに組み込むGitHookを作成

前のセクションでブランチ名にチケット番号があるという前提ができました。
これ以降は、ブランチ名にチケット番号が入っているので、それをコミットメッセージにチケット番号を入力すればいいだけです。

#!/bin/sh

if [ "$2" == "" ] ; then
    ticketNumber=`git branch | grep "*" | awk '{print $2}' | sed -e "s/^\([0-9]*\).*/\1/g"`
    if [ -n "$ticketNumber" ]; then
        mv $1 $1.tmp
        echo "fix #${ticketNumber}" > $1
        cat $1.tmp >> $1
    else
	# チケット番号がないブランチ
	echo -e "チケット番号がありません。"
	exit 1
    fi
fi

さっきのpost-checkoutを流用しただけのお手軽ファイルを作成します。
ブランチ名からチケット番号らしき数字を取得し、コミットメッセージの最初にfix #チケット番号 を追加しています。

※すべてのコミットにこれが適用されます。


3. 作成したGitHooksを共有する

基本的に、GitHookは「.git/hooks」で生成・管理されるので、Git管理下にありません。
ですが、今回はチームメンバーと共有しなくては意味がありません。

なんと便利なことにコマンド一つでGitHooksのパスを変更することが可能なのです!

git config core.hooksPath <githooks dir>

超簡単!一番最後の引数にgithookしてほしいファイルが入ったディレクトリを指定するだけです。今回は、プロジェクトルートに「.githooks」ディレクトリを作成し、その中に1と2で作成したファイルを入れます。

なので、この場合下記コマンドを実行するだけで、良いわけです。

git config core.hooksPath .githooks

3 – 1 ダブルクリックだけで済むようにする

このコマンドをターミナルに入ってプロジェクトのあるディレクトリで実行してくれ!というのも簡単ですが、さすがに不親切な気がするので、batファイルを作っておきます。(windowsユーザーがほとんどなので)

@echo off
cd %~dp0
git config core.hooksPath .githooks

cd %~dp0 でこのbatファイルが存在するディレクトリに移動
そして先ほどのコマンドを実行

ただそれだけのファイルですが、あると便利だしみんな助かるので作りましょう。

※作成したbatファイルと.githooksが同じ階層にある前提です。

3 – 2 一時無効化batファイルを作成

急な修正やチケットがない変更…その他もろもろの理由で今回作成したgithooksが鬱陶しく感じる時があるかもしれません。
ですので、事前に一時無効化できるようにしておきます。

chcp 65001
@echo off
cd %~dp0
git config core.hooksPath .git/
set /p x="GitHooksの機能を一時的に無効化しました。元に戻しますか?(y/n):"
if "%x%" == "y" (
    git config core.hooksPath .githooks
)

このファイルをダブルクリックすることで、一時的に無効化されます。
その後はキー入力次第で元に戻すかどうか選択することができます。

一番上に「chcp 65001」と入れておくことで、日本語の文字化けを防げます。

hooksPathを./gitにしておくことで、何も読み込まれない状況を作り出しています。これは、./git/hooks/にはあるが./gitではファイルが見つからないため、動作しません。ですので、新しく空の「empty_githooks」ディレクトリを作成し以下のようにすることも一つの策です。

git config core.hooksPath empty_githooks

最後に

これでひとまず、プログラム側でどうにかできたので、後は運用面で浸透させる必要があります。

もしかすると、ここが一番肝で難しいことかもしれませんね…

それでは、またお会いしましょう!(^^)!

GitHubとRedmineを連携させてチケット管理を楽にする方法

オンラインコンサルタントの直井です(^^♪

4月になり、寒暖差がとても激しいですが、いかがお過ごしでしょうか!(^^)!
冬用のアウターを完全に収納してしまったので、仕事の行はまだしも、帰りに凍えながら通勤しています(T_T)

さて、今回は表題の通りチケット管理をもっと楽に!というテーマでお話できればと思います。


チケット閉じ忘れ問題

「 問題が発生した!チケット作ろう! 」と考えチケットを追加してくれる方はかなりいらっしゃるのですが、それを終了にすることを忘れがちです….なので、「本当は終わっている」のに終了になっていないチケットが多数存在していました。

それによって、同じ意味のチケットが2つ以上存在してしまったり..なんてことも…

遠足でよく聞くフレーズのように、「チケットを閉じるまでが修正」これを開発メンバー全員に毎日チャットで送れば少しは改善するかもしれません。
しかし、こんな非効率的なことをしても互いにうっとおしいだけで、またチケットが閉じられなくなる日が目に見えています。


チケットとGitを連携させる

どうすれば、チケットの閉じ忘れが解消されるか?

修正は、いずれ origin/main ブランチにマージされます。
それは、チケットが終了したことを意味します。
ということは….
「 origin/mainにマージされた内容(コミットメッセージ等)にチケット番号があればそのチケットは終了 」にすればいいのでは?

そう考えた我々はアマゾンの奥地へ向かった…


1. 環境

1 Git(remote)
・GitHub
2 チケット管理
・Redmine( 4.1.1.stable )

今回は上記のような構成でお送りします。バージョンなどの違いで少し手順が変わっているかもしれません。

また、プラグインを導入する際に、うまくインストールできない場合があると思います。その際は、下記リンクの記事を参照ください。
https://oc-technote.com/%e6%a5%ad%e5%8b%99%e5%8a%b9%e7%8e%87up/redmine%e3%81%ab%e3%83%97%e3%83%a9%e3%82%b0%e3%82%a4%e3%83%b3%e3%82%92%e5%85%a5%e3%82%8c%e3%82%8b%ef%bc%81/


2. 手順

2 – 1 redmine_github_hookについて

今回利用するプラグインは「 redmine_github_hook 」です。
こちらのプラグインがどのように動作するか、簡単に説明すると下記のような形です。

①:プラグインを発火させるためのURLを踏む
②:git fetchでリモートリポジトリとプラグイン内部にあるローカルリポジトリを同期
③:コミット内容を読み込んで、チケットのアクションを実行(終了や関連付け)

サクッと①を行うことで自動的に②、③が実行されます。詳しいライフサイクルはこちら

なんて便利なプラグインなのでしょうか!デフォルトで入っていてもいいような気がしますが…
しかし、今回実現したいことをやろうとするといまいちな箇所があります。

・②を実行すると、developや開発途中のリモートブランチなどの変更も読み取って終了になってしまう(git fetchですべてのブランチの変更をくみ取るため)


2 – 2 Redmineにプラグインを追加

redmineアプリディレクトリのpluginsディレクトリ配下にプラグインファイルを配置します。(bitnamiスタックの場合「~/stack/apps/redmine/htdocs/plugins」の可能性が高いです。)
また、今回使用するプラグインは「redmine_github_hook」ですが、ほかのプラグインも同様の手順でインストールすることができます。

今回、マスターにブランチにマージされた内容だけをくみ取りたいので、下記URLからプラグインを追加
https://github.com/asurann123/redmine_github_hook

git clone https://github.com/asurann123/redmine_github_hook.git

このリポジトリで変更している箇所は、「app/services/github_hook/updater.rb」の最下部にある関数を変更しています。環境によってはmainブランチだったりするかもしれないので、適宜変更をお願いします。(Gitがうまく吸収してくれる可能性あるかも?)

ここまで来たら、後はreadmeにある内容の通りです。

アプリケーションのトップディレクトリに戻っておく(bitnamiの場合)

cd ~/stack/apps/redmine/htdocs/

gem追加

gem "redmine_github_hook"

インストール

 bundle install --no-deployment

マイグレーション

 bundle exec rake redmine:plugins:migrate RAILS_ENV=production

再起動(bitnamiの場合)

sudo /opt/bitnami/ctlscript.sh restart

2 – 3 プラグイン用にリポジトリをclone

bareオプションを追加して適当な場所にリポジトリをクローン

 git clone --bare <Repos URL or SSH>

リモートを追加

git remote add origin <Repos URL or SSH>

2 – 4 プロジェクトにリポジトリを設定

プロジェクトの「設定」から「リポジトリ」を選択し、「新しいリポジトリ」をクリック

先ほどクローンしたpathを入力する。メインであればメインにチェックし、識別子があると後々わかりやすいが、記入しなくてもいい

2 – 5 発火URLの設定

ここまででとりあえずGitとRedmineの連携設定は完了です。しかし、動作させるには発火URLを踏む必要があります。

ほかのサイトを参考にすると、Github Actionsを利用したりしています。
今回は、デプロイの1セクションにこれを追加しようと思います。

簡単にPythonファイルを作成して、デプロイのお好きなタイミングにこのPythonファイルを実行するだけです。AWSをご利用の場合、CodePiplineなどを利用されていると思いますので、その間にLambdaで発火させるだけです。


import requests

response = requests.post('https://<your domain>/github_hook?project_id=<project_id>',params={"project_id":"<project_id>"})

3 . 最後に

かなり超大作になってしまいました💦しかし、同じような内容でお困りの方が少しでも参考になればと思います。

一年前に作業した内容なので、もしかすると情報が古い可能性があります。

次回は、「 コミットメッセージにチケット番号がない場合に警告を出すGithook 」編でお会いしましょう!

SQL処理速度関連の基礎的なメモ

SQLの記述で忘れがちなことが多いのでメモしておきます。
(ついつい忘れがちなんですが、とりあえず下記内容だけは守りたいと思います…)

・否定形(NOT)のSQLはインデックスが使用されないので避けるべき

・where or はインデックススキャンのコスト増? ORではなくIN句かEXISTS句を使うべき (CASE式のときならORもOK?)
※INとEXISTSの違い・・・ IN:副問合せ→親問合せ EXISTS:親問合せ→副問合せ の順番

・可能であればSELECT * FROM ~ ではなくカラムを指定して取得する項目を狭める

・スロークエリにならないか不安なSQL内容があれば、EXPLAINを利用して事前に調査しておく



【Androidアプリ開発】serviceについて

₋ serviceとは

バックグラウンドで動作して操作を行うアプリコンポーネント。

アクティビティから独立してバックグラウンドで処理を続ける。
アクティビティが終了しても処理が続く。

フォアグラウンドサービス、バックグラウンドサービス、バインドされたサービスがある。

₋ 利用手順

①serviceクラスを継承したクラスを作成

②AndroidManufiext.xmlにサービスを登録
<タグの属性>
android:enabled 登録したサービスを利用可能か
android:exporgted 作成したサービスを外部アプリから利用可能か

③onStartCommand()メソッドにバックグラウンドで行う処理を記述
<戻り値で使用する定数(サービスが強制終了されたときにどうするか指定)>
START_NOT_STICKY, START_STICKY, START_REDELIVER_INTENT

④アクティビティからこのクラスを起動
Intentクラスをnewする、startService()、stopService()