スタッフブログ
熱心な参加者の皆さんにささえられて、今度の土曜(2011/07/23)で第17回目になるXOOPS Cubeサタデーラボですが、facebookページがあるのご存知ですか?
けっこう前につくってたんですが、ryus.co.jp でお知らせするのをすっかり忘れてました(^^;
■Facebookでサタラボ最新情報を入手しませんか?
ちょっと今回は実験的に参加申込もfacebook ページからできるようにしてみました。
# 今回は更に実験的に、ATNDでも受け付けてます。
どこでもお好みの方法で申込していただければと思ったんですが、あちこちで申込できるとかえってどこで申込したらいいか迷いますかね?
XOOPS Cubeにありそうでなかった機能の一つが、「モジュール一括アップデート」機能です。 モジュールのアップデートを行うとなると、個々のモジュールのアップデートページで ひとつひとつ対応していくことになります。 決まった作業なのに、手作業とは手間ですね。 そこで、モジュールアップデート作業を自動で処理できるようなプラグイン(プリロード)を作りました。
RapidModuleUpdateは、モジュール一括アップデート機能をモジュール管理に追加するプラグイン(プリロード)です。 まずは、そのでも動画をごらんください。30秒で終わります。
このように、RapidModuleUpdateは複数のモジュールアップデート作業を自動化し、なんとも手間いらずです。
インストールの方法も簡単です。FTPでRapidModuleUpdate.class.phpを/preloadフォルダに置くだけで完了です。 あとは、管理者権限でログインして、「モジュール管理」を開いてみてください。 上の動画にあったような一括アップデートのチェックボックスが出ているはずです。
CentOSのPHPのデフォルトバージョンは5.1です。なので、PHP5.2にアップデートすることがよくあるので備忘録として手順をまとめておきます。
手順
とりあえずサーバを停止
sudo /etc/init.d/httpd stop sudo /etc/init.d/mysqld stop
リポジトリを追加する。
sudo vi /etc/yum.repos.d/utterramblings.repo
[utterramblings] name=Jason's Utter Ramblings Repo baseurl=http://www.jasonlitka.com/media/EL$releasever/$basearch/ enabled=1 gpgcheck=1 gpgkey=http://www.jasonlitka.com/media/RPM-GPG-KEY-jlitka
アップデートをかける。
sudo yum update php -y
実際はここではまりました。ハマった詳細は後述。
リポジトリをOFFにする。enabledを0に書き換えます。
sudo vi /etc/yum.repos.d/utterramblings.repo
[utterramblings] name=Jason's Utter Ramblings Repo baseurl=http://www.jasonlitka.com/media/EL$releasever/$basearch/ enabled=0 gpgcheck=1 gpgkey=http://www.jasonlitka.com/media/RPM-GPG-KEY-jlitka
サーバを起動する。
sudo /etc/init.d/httpd start sudo /etc/init.d/mysqld start
はまったところ
yum update phpで次のようなエラーが出てアップデートできずはまりました。
$ sudo yum update php Loaded plugins: fastestmirror Loading mirror speeds from cached hostfile * addons: ftp.yz.yamagata-u.ac.jp * base: ftp.yz.yamagata-u.ac.jp * extras: ftp.yz.yamagata-u.ac.jp * updates: ftp.yz.yamagata-u.ac.jp Excluding Packages from CentOS-5 - Base Finished Setting up Update Process Resolving Dependencies --> Running transaction check --> Processing Dependency: php = 5.1.6-27.el5_5.3 for package: php-devel ---> Package php.x86_64 0:5.2.16-jason.1 set to be updated --> Processing Dependency: php-common = 5.2.16-jason.1 for package: php --> Processing Dependency: php-cli = 5.2.16-jason.1 for package: php --> Running transaction check ---> Package php-cli.x86_64 0:5.2.16-jason.1 set to be updated --> Processing Dependency: php-common = 5.1.6-27.el5_5.3 for package: php-pdo --> Processing Dependency: php-common = 5.1.6-27.el5_5.3 for package: php-ldap --> Processing Dependency: php-common = 5.1.6-27.el5_5.3 for package: php-mbstring --> Processing Dependency: php-common = 5.1.6-27.el5_5.3 for package: php-gd --> Processing Dependency: php-common = 5.1.6-27.el5_5.3 for package: php-mysql ---> Package php-common.x86_64 0:5.2.16-jason.1 set to be updated ---> Package php-devel.x86_64 0:5.2.16-jason.1 set to be updated --> Running transaction check ---> Package php-gd.x86_64 0:5.2.16-jason.1 set to be updated ---> Package php-ldap.x86_64 0:5.2.16-jason.1 set to be updated ---> Package php-mbstring.x86_64 0:5.2.16-jason.1 set to be updated ---> Package php-mysql.x86_64 0:5.2.16-jason.1 set to be updated --> Processing Dependency: libmysqlclient.so.16(libmysqlclient_16)(64bit) for package: php-mysql --> Processing Dependency: libmysqlclient.so.16()(64bit) for package: php-mysql ---> Package php-pdo.x86_64 0:5.2.16-jason.1 set to be updated --> Running transaction check --> Processing Dependency: libmysqlclient.so.15()(64bit) for package: mysql-connector-odbc --> Processing Dependency: libmysqlclient.so.15()(64bit) for package: perl-DBD-MySQL --> Processing Dependency: libmysqlclient.so.15()(64bit) for package: mysql-server --> Processing Dependency: libmysqlclient.so.15()(64bit) for package: libdbi-dbd-mysql --> Processing Dependency: libmysqlclient.so.15(libmysqlclient_15)(64bit) for package: mysql-connector-odbc --> Processing Dependency: libmysqlclient.so.15(libmysqlclient_15)(64bit) for package: perl-DBD-MySQL --> Processing Dependency: libmysqlclient.so.15(libmysqlclient_15)(64bit) for package: mysql-server --> Processing Dependency: libmysqlclient.so.15(libmysqlclient_15)(64bit) for package: libdbi-dbd-mysql --> Processing Dependency: libmysqlclient_r.so.15()(64bit) for package: mysql-connector-odbc --> Processing Dependency: libmysqlclient_r.so.15()(64bit) for package: mysql-server --> Processing Dependency: libmysqlclient_r.so.15()(64bit) for package: MySQL-python --> Processing Dependency: libmysqlclient_r.so.15(libmysqlclient_15)(64bit) for package: mysql-connector-odbc --> Processing Dependency: libmysqlclient_r.so.15(libmysqlclient_15)(64bit) for package: mysql-server --> Processing Dependency: libmysqlclient_r.so.15(libmysqlclient_15)(64bit) for package: MySQL-python ---> Package mysql.x86_64 0:5.1.52-jason.1 set to be updated --> Running transaction check ---> Package mysql-server.x86_64 0:5.1.52-jason.1 set to be updated ---> Package mysqlclient15.x86_64 0:5.0.91-1.jason.1 set to be updated --> Finished Dependency Resolution Dependencies Resolved ======================================================================================================================================================================================================== Package Arch Version Repository Size ======================================================================================================================================================================================================== Updating: php x86_64 5.2.16-jason.1 utterramblings 3.8 M Installing for dependencies: mysqlclient15 x86_64 5.0.91-1.jason.1 utterramblings 2.0 M Updating for dependencies: mysql x86_64 5.1.52-jason.1 utterramblings 3.5 M mysql-server x86_64 5.1.52-jason.1 utterramblings 13 M php-cli x86_64 5.2.16-jason.1 utterramblings 2.6 M php-common x86_64 5.2.16-jason.1 utterramblings 522 k php-devel x86_64 5.2.16-jason.1 utterramblings 557 k php-gd x86_64 5.2.16-jason.1 utterramblings 348 k php-ldap x86_64 5.2.16-jason.1 utterramblings 63 k php-mbstring x86_64 5.2.16-jason.1 utterramblings 1.4 M php-mysql x86_64 5.2.16-jason.1 utterramblings 280 k php-pdo x86_64 5.2.16-jason.1 utterramblings 169 k Transaction Summary ======================================================================================================================================================================================================== Install 1 Package(s) Upgrade 11 Package(s) Total size: 28 M Is this ok [y/N]: y Downloading Packages: Running rpm_check_debug Running Transaction Test Finished Transaction Test Transaction Check Error: file /etc/my.cnf from install of mysql-5.1.52-jason.1.x86_64 conflicts with file from package mysql-5.0.77-4.el5_5.4.i386 file /usr/share/man/man1/my_print_defaults.1.gz from install of mysql-5.1.52-jason.1.x86_64 conflicts with file from package mysql-5.0.77-4.el5_5.4.i386 file /usr/share/man/man1/mysql.1.gz from install of mysql-5.1.52-jason.1.x86_64 conflicts with file from package mysql-5.0.77-4.el5_5.4.i386 file /usr/share/man/man1/mysql_config.1.gz from install of mysql-5.1.52-jason.1.x86_64 conflicts with file from package mysql-5.0.77-4.el5_5.4.i386 file /usr/share/man/man1/mysql_find_rows.1.gz from install of mysql-5.1.52-jason.1.x86_64 conflicts with file from package mysql-5.0.77-4.el5_5.4.i386 file /usr/share/man/man1/mysql_waitpid.1.gz from install of mysql-5.1.52-jason.1.x86_64 conflicts with file from package mysql-5.0.77-4.el5_5.4.i386 file /usr/share/man/man1/mysqlaccess.1.gz from install of mysql-5.1.52-jason.1.x86_64 conflicts with file from package mysql-5.0.77-4.el5_5.4.i386 file /usr/share/man/man1/mysqladmin.1.gz from install of mysql-5.1.52-jason.1.x86_64 conflicts with file from package mysql-5.0.77-4.el5_5.4.i386 file /usr/share/man/man1/mysqldump.1.gz from install of mysql-5.1.52-jason.1.x86_64 conflicts with file from package mysql-5.0.77-4.el5_5.4.i386 file /usr/share/man/man1/mysqlshow.1.gz from install of mysql-5.1.52-jason.1.x86_64 conflicts with file from package mysql-5.0.77-4.el5_5.4.i386 file /usr/share/mysql/charsets/Index.xml from install of mysql-5.1.52-jason.1.x86_64 conflicts with file from package mysql-5.0.77-4.el5_5.4.i386 file /usr/share/mysql/charsets/cp1250.xml from install of mysql-5.1.52-jason.1.x86_64 conflicts with file from package mysql-5.0.77-4.el5_5.4.i386 file /usr/share/mysql/czech/errmsg.sys from install of mysql-5.1.52-jason.1.x86_64 conflicts with file from package mysql-5.0.77-4.el5_5.4.i386 file /usr/share/mysql/danish/errmsg.sys from install of mysql-5.1.52-jason.1.x86_64 conflicts with file from package mysql-5.0.77-4.el5_5.4.i386 file /usr/share/mysql/dutch/errmsg.sys from install of mysql-5.1.52-jason.1.x86_64 conflicts with file from package mysql-5.0.77-4.el5_5.4.i386 file /usr/share/mysql/english/errmsg.sys from install of mysql-5.1.52-jason.1.x86_64 conflicts with file from package mysql-5.0.77-4.el5_5.4.i386 file /usr/share/mysql/estonian/errmsg.sys from install of mysql-5.1.52-jason.1.x86_64 conflicts with file from package mysql-5.0.77-4.el5_5.4.i386 file /usr/share/mysql/french/errmsg.sys from install of mysql-5.1.52-jason.1.x86_64 conflicts with file from package mysql-5.0.77-4.el5_5.4.i386 file /usr/share/mysql/german/errmsg.sys from install of mysql-5.1.52-jason.1.x86_64 conflicts with file from package mysql-5.0.77-4.el5_5.4.i386 file /usr/share/mysql/greek/errmsg.sys from install of mysql-5.1.52-jason.1.x86_64 conflicts with file from package mysql-5.0.77-4.el5_5.4.i386 file /usr/share/mysql/hungarian/errmsg.sys from install of mysql-5.1.52-jason.1.x86_64 conflicts with file from package mysql-5.0.77-4.el5_5.4.i386 file /usr/share/mysql/italian/errmsg.sys from install of mysql-5.1.52-jason.1.x86_64 conflicts with file from package mysql-5.0.77-4.el5_5.4.i386 file /usr/share/mysql/japanese/errmsg.sys from install of mysql-5.1.52-jason.1.x86_64 conflicts with file from package mysql-5.0.77-4.el5_5.4.i386 file /usr/share/mysql/korean/errmsg.sys from install of mysql-5.1.52-jason.1.x86_64 conflicts with file from package mysql-5.0.77-4.el5_5.4.i386 file /usr/share/mysql/norwegian-ny/errmsg.sys from install of mysql-5.1.52-jason.1.x86_64 conflicts with file from package mysql-5.0.77-4.el5_5.4.i386 file /usr/share/mysql/norwegian/errmsg.sys from install of mysql-5.1.52-jason.1.x86_64 conflicts with file from package mysql-5.0.77-4.el5_5.4.i386 file /usr/share/mysql/polish/errmsg.sys from install of mysql-5.1.52-jason.1.x86_64 conflicts with file from package mysql-5.0.77-4.el5_5.4.i386 file /usr/share/mysql/portuguese/errmsg.sys from install of mysql-5.1.52-jason.1.x86_64 conflicts with file from package mysql-5.0.77-4.el5_5.4.i386 file /usr/share/mysql/romanian/errmsg.sys from install of mysql-5.1.52-jason.1.x86_64 conflicts with file from package mysql-5.0.77-4.el5_5.4.i386 file /usr/share/mysql/russian/errmsg.sys from install of mysql-5.1.52-jason.1.x86_64 conflicts with file from package mysql-5.0.77-4.el5_5.4.i386 file /usr/share/mysql/serbian/errmsg.sys from install of mysql-5.1.52-jason.1.x86_64 conflicts with file from package mysql-5.0.77-4.el5_5.4.i386 file /usr/share/mysql/slovak/errmsg.sys from install of mysql-5.1.52-jason.1.x86_64 conflicts with file from package mysql-5.0.77-4.el5_5.4.i386 file /usr/share/mysql/spanish/errmsg.sys from install of mysql-5.1.52-jason.1.x86_64 conflicts with file from package mysql-5.0.77-4.el5_5.4.i386 file /usr/share/mysql/swedish/errmsg.sys from install of mysql-5.1.52-jason.1.x86_64 conflicts with file from package mysql-5.0.77-4.el5_5.4.i386 file /usr/share/mysql/ukrainian/errmsg.sys from install of mysql-5.1.52-jason.1.x86_64 conflicts with file from package mysql-5.0.77-4.el5_5.4.i386 Error Summary -------------
どうやらこれは、mysql.x86_64とmysql.i386がコンフリクトしているとのことのようです。 mysql.i386を使ってない場合、yum removeしていいとのことでした。
mysqlのどもパッケージは入っているか確認します。
$ yum list installed | grep mysql libdbi-dbd-mysql.x86_64 0.8.1a-1.2.2 installed mysql.i386 5.0.77-4.el5_5.4 installed mysql.x86_64 5.0.77-4.el5_5.4 installed mysql-connector-odbc.x86_64 3.51.26r1127-1.el5 installed mysql-server.x86_64 5.0.77-4.el5_5.4 installed php-mysql.x86_64 5.1.6-27.el5_5.3 installed
.i386なのはmysql.i386だけでした。次に、yum remove(アンインストール)するまえに本当に、使ってないか確認します。
$ mysql --version mysql Ver 14.14 Distrib 5.1.52, for redhat-linux-gnu (x86_64) using readline 5.1
i386は使われていなく、x86_64が使われていることが分かりましたので、yum removeします。
$ sudo yum remove mysql.i386 Loaded plugins: fastestmirror Setting up Remove Process Resolving Dependencies --> Running transaction check ---> Package mysql.i386 0:5.0.77-4.el5_5.4 set to be erased --> Finished Dependency Resolution Dependencies Resolved ======================================================================================================================================================================================================== Package Arch Version Repository Size ======================================================================================================================================================================================================== Removing: mysql i386 5.0.77-4.el5_5.4 installed 7.9 M Transaction Summary ======================================================================================================================================================================================================== Remove 1 Package(s) Reinstall 0 Package(s) Downgrade 0 Package(s) Is this ok [y/N]: y Downloading Packages: Running rpm_check_debug Running Transaction Test Finished Transaction Test Transaction Test Succeeded Running Transaction Erasing : mysql 1/1 Removed: mysql.i386 0:5.0.77-4.el5_5.4
アンインストールされたようです。
一応、yum list installedでremoveされたか確認します。
$ yum list installed | grep mysql libdbi-dbd-mysql.x86_64 0.8.1a-1.2.2 installed mysql.i386 5.0.77-4.el5_5.4 installed mysql.x86_64 5.0.77-4.el5_5.4 installed mysql-connector-odbc.x86_64 3.51.26r1127-1.el5 installed mysql-server.x86_64 5.0.77-4.el5_5.4 installed php-mysql.x86_64 5.1.6-27.el5_5.3 installed
この後、再度 yum update phpをしたらうまくPHPがアップデートされました。
SSHにログインしようとするとToo many authentication failuresと言われる
MacのターミナルでSSHにログインしようとすると次のようなメッセージが出てログインできません。
Received disconnect from XXX.XXX.XXX.XXX: 2: Too many authentication failures for *username*
このように解決しました
Macのターミナルを起動
$ ssh-add -D $ open ~/.ssh/config # お使いのエディタがあればそれでひらいてください。 # configファイルが無ければ作ってください。テキストでいいです。
configファイルの最初に以下の行を追加
IdentitiesOnly yes
svn upするとsvn: access to '*url*' forbiddenと言われる
svn upしようとすると、svn: access to '*url*' forbiddenと言われて、アップデートが失敗しました。 他のマシンで同じようにsvn upしたらできたので、問題が起きたPCの問題だと分かりました。
このように解決しました
前回、svn upしたユーザが違っていたのが問題だったようです。 前回ユーザ suin で sudo svn up していたのに、今回は root で svn up しようとしました。 そこで、suinユーザで sudo svn up したら、問題なくアップデートできました。
mainfile.phpはXOOPSの設定ファイルで、XOOPSのパスやデータベースのアカウント情報などが書かれています。これらの設定情報は、当然 環境に依存しています。
開発フローを、開発→ステージング→プロダクションという流れでやる場合、同じサイトが少なくとも3つ以上できてしまいます。そうすると、mainfile.phpも複数できあがるわけです。ところが、XOOPS Cube Legacyはサイトプロフィールという概念がないため、mainfile.phpの取り扱いは悩ましいものがあります。(他のCMS、Drupalなどではあると聞いています。)
複数のmainfile.php問題を解決する方法はいくつか考えられます。
1. どの環境もそっくりの環境にする
XOOPS_ROOT_PATHなどはdirname(__FILE__)で解決し、データベースのアカウント情報はどのサーバでも共通のものにするという手法です。 ただ、この方法だとチームで開発に取り組む場合、あまりうまくいきません。SVNで管理しているサイトだと、お互いのmainfile.phpを上書きしあう心配があります。
2. mainfile.phpはSVNで管理しない
mainfile.phpは環境依存が強いため、SVNでは管理しないようにしておく方法があります。こうしておくと、チームでお互いのmainfile.phpを上書きする心配もありません。ところが、この方法では、リポジトリにmainfile.phpがないのが欠点になります。mainfile.phpが無いので、チェックアウトしてきても直ぐに動かせません。また、そのサイト全体で使われている定数をmainfile.phpに書いておくことがありますが、定数をいちいち手動で追加しなければなりません。(本来、環境依存ではない定数は、/settings/definition.phpに書くべきですが。)
そこで、今回紹介するのは、mainfile.phpをサイトごとに切り替える方法です。
まず、サイトごとのmainfile.phpを適当に名前を変更して、settingsの下に置きます。 例えば、今ローカル環境にあるmainfile.phpは開発環境なので、名前をmainfile.dev.phpに変えて、settingsに移動します。
. ├── mainfile.php ├── settings │ ├── mainfile.dev.php │ ├── mainfile.stag.php │ └── mainfile.prod.php (略)
次に、XOOPS_ROOT_PATHに空のmainfile.phpを作り、そこに下のコードをコピペします。
<?php require XoopsProfiler::getProfile(); class XoopsProfiler { // ホスト名 => ファイル名 protected static $map = array( 'suinasia.local' => 'mainfile.dev.php', // 開発 'stag.suin.asia' => 'mainfile.stag.php', // ステージング 'suin.asia' => 'mainfile.prod.php', // プロダクション ); public static function getProfile() { $xoopsRootPath = dirname(__FILE__); $serverName = $_SERVER['SERVER_NAME']; $ds = DIRECTORY_SEPARATOR; if ( !isset(self::$map[$serverName]) ) { trigger_error("Profile not found."); die; } $mainfile = self::$map[$serverName]; $path = $xoopsRootPath.$ds.'settings'.$ds.$mainfile; return $path; } }
あとは、$map配列のところを適宜書き換えるだけです。 配列のキーはホスト名で、値はそのホストで使うmainfile.phpになります。
上のコードを少し書き換えれば、開発環境でも複数のデータベースを簡単に切り替えたり、いろいろ応用できると思います。
MySQLには遅いクエリーを改善するために、指定した秒数以上かかったクエリーをログに取っておいてくれる機能があります。それをスロークエリーログといいます。この記事では、スロークエリーログを有効にして、実際に作られたログを確認するところまで順を追って説明します。
ひとまずrootになる。
sudo su - root # とか su - root
MySQLの設定ファイルを編集します。
vim /etc/my.cnf # vim: command not foundな人は vi /etc/my.cnf
iとタイプして編集モードへ。
slow_query_log=1 # スロークエリーログを有効にします。ONとかOnと書いても有効にならないので注意。 slow_query_log_file=mysql-slow.log # フルパスじゃないとたぶん、datadirにできます。 long_query_time=1 # 秒数で指定。この例では1秒以上かかるクエリはログに残る
escキーを押して、:wqとタイプ後Enterで上書き保存
MySQLの再起動を忘れずに。
/etc/rc.d/init.d/mysqld restart
MySQLにログインしてみよう。
mysql -u root # とか mysql -u root -p
2秒かかるクエリーを実行してみる。
mysql> SELECT SLEEP(2); +----------+ | SLEEP(2) | +----------+ | 0 | +----------+ 1 row in set (2.00 sec)
MySQLから抜けます。
mysql> exit
slow_query_log_fileがあるディレクトリに移動。
cd /var/lib/mysql/
中身を見てみよう。
cat mysql-slow.log # Time: 101021 13:04:49 # User@Host: root[root] @ localhost [] # Query_time: 2.002632 Lock_time: 0.000000 Rows_sent: 1 Rows_examined: 0 SET timestamp=1287633889; SELECT SLEEP(2);
ちゃんとログが取れてるのが確認できました。
MySQLのInnoDBはトランザクションが使えたり、行ロックが使えたりして、データの整合性の点でMyISAMに比べて優れています。 業務系アプリになると、データの整合性が重視されることも多く、トランザクションを使うことも増えます。
今回、そのトランザクションを使っていて、思いもよらないクエリがきっかけで、テーブルにロックが掛かってハマりました。
テーブル構造はこのような感じ...。
CREATE TABLE `working` ( `id` int(11) NOT NULL AUTO_INCREMENT, `col` int(11) NOT NULL DEFAULT '0', PRIMARY KEY (`id`), KEY `col` (`col`) ) ENGINE=InnoDB;
もう一個まったく同じテーブルを作ります。
CREATE TABLE `backup` LIKE `working`;
workingにデータを入れます。
INSERT INTO `working` (`col`) VALUES ('1'), ('2'), ('3'); SELECT * FROM `working`; +----+-----+ | id | col | +----+-----+ | 1 | 1 | | 2 | 2 | | 3 | 3 | +----+-----+
ちなみに、分離レベルを見ておきます。
SELECT @@tx_isolation; +-----------------+ | @@tx_isolation | +-----------------+ | REPEATABLE-READ | +-----------------+
さて、もう察しが付いているかもしれませんが、これからworkingからbackupにデータを移すクエリを実行したいと思います。
クライアント1
クライアント1は、BEGINしてトランザクションを開始し、INSERT INTO t SELECT ... FROM s WHERE...
を使って、データコピーのクエリを実行します。
mysql> BEGIN;
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO `backup` (`col`) SELECT `col` FROM `working` WHERE `col` = 3;
Query OK, 1 row affected (0.00 sec)
Records: 1 Duplicates: 0 Warnings: 0
この処理は一瞬で終わりますが、COMMITやROLLBACKせず、トランザクションはそのままにしておきます。
クライアント2
クライアント2では、普通にworkingにINSERTします。
mysql> BEGIN;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT * FROM `backup`;
Empty set (0.00 sec)
mysql> INSERT INTO `working` (`col`) VALUES ('4');
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
まず、backupテーブルはまだ空です。これはクライアント1がコミットせずトランザクションを維持しているからです。しかし、ここでworkへのINSERTが長く待たされます。挙句に、「Lock wait timeout exceeded」というエラーが出て、クライアント2のトランザクションが終了してしまいます。
一見すると、とても奇妙な挙動です。クライアント1は、サブクエリーでworkingテーブルにSELECT
しているだけです。UPDATE
やSELECT ... FOR UPDATE
をかけているわけではないからです。
ところが、良く調べてみると、INSERT INTO SELECT FROMの形でSELECTされたテーブルにロックが掛かることがあるとマニュアルに書いてありました。
INSERT INTO T SELECT ... FROM S WHERE ... は T に挿入された各行に、ギャップロックなしの排他インデックスレコードロックを設定します。innodb_locks_unsafe_for_binlog が有効であるかトランザクション遮断レベルが READ COMMITTED である場合には、InnoDB は S での検索を一貫性読み取り (ロックなし) として行います。それ以外の場合、InnoDB は S から取得した行に共有ネクストキーロックを設定します。InnoDB は後者の場合にロックを設定する必要があります。バックアップからの前進復旧では、すべての SQL ステートメントはそれが元々行われたのとまったく同じ方法で実行されなければいけません。
これを読む限りでは、workingテーブルにネクストキーロックがかかってしまったようです。注目して欲しいのは、クライアント1のクエリの`col` = 3
と、クライアント2の(`col`) VALUES ('4')
です。ネクストキーロックの範囲や発動条件はよく調べていないのでなんとも言えませんが、colの数字が3と4で隣り合っています。これによってロックが発動したようです。
ためしに、下のように隣接していない場合はロックが掛かりません。
# クライアント1 mysql> BEGIN; Query OK, 0 rows affected (0.00 sec) mysql> INSERT INTO `backup` (`col`) SELECT `col` FROM `working` WHERE `col` = 1; Query OK, 1 row affected (0.00 sec) Records: 1 Duplicates: 0 Warnings: 0 # クライアント2 mysql> BEGIN; Query OK, 0 rows affected (0.00 sec) mysql> INSERT INTO `working` (`col`) VALUES ('4'); Query OK, 1 row affected (0.00 sec)
また比較として、単なるSELECT
やINSERT
では、ロックはかかりません。
# クライアント1 mysql> BEGIN; Query OK, 0 rows affected (0.00 sec) mysql> INSERT INTO `working` (`col`) VALUES ('3'); Query OK, 1 row affected (0.00 sec) # クライアント2 mysql> BEGIN; Query OK, 0 rows affected (0.00 sec) mysql> INSERT INTO `working` (`col`) VALUES ('4'); Query OK, 1 row affected (0.00 sec)
もし、以下の条件に当てはまる処理は、INSERT INTO SELECT FROM
のサブクエリを独立させてPHPで結果をINSERTに渡すような実装にしたほうが良いかもしれません。
- workingは頻繁にINSERTされる。(そして待たされたり、タイムアウトしては困る。)
- INSERT INTO SELECT FROMのあるトランザクションが非常に長い。
- クライアント2のINSERTはクライアント1のトランザクションに全く関係ないデータである。
余談
今回はトランザクションにはまりましたが、ROLLBACKは便利そうです。大量のテストデータとテストパターンがあって、毎回DBをリストアしてテストしなおすような計画では、テストが完了するごとにROLLBACKすると良さそうです。一瞬でDBがもとに戻ります。
PHPにはSSH2の関数が用意されています。ここでは、CentOSにSSH2を入れる手順を書いておきます。試行錯誤込みで書いてますので、手っ取り早くうまく言ったコマンドだけ知りたい人はこのセクションの最後に飛んでください。
SSH2はPECLの拡張モジュールなので、下記のコマンドを実行すればいいとのこと。
pecl install ssh2-beta
実行してみた。
bash: pecl: command not found
...(-_-;;;
PECLが入ってなかったとは。PECLを入れるために下記のコマンドを実行しました。
yum -y install php-pear
すると、こんどは、Missing Dependencyのエラーが。なんという失敗続き;;
php-devel-5.1.6-27.el5.x86_64 from base has depsolving problems --> Missing Dependency: php = 5.1.6-27.el5 is needed by package php-devel-5.1.6-27.el5.x86_64 (base) Error: Missing Dependency: php = 5.1.6-27.el5 is needed by package php-devel-5.1.6-27.el5.x86_64 (base) You could try using --skip-broken to work around the problem You could try running: package-cleanup --problems package-cleanup --dupes rpm -Va --nofiles --nodigest The program package-cleanup is found in the yum-utils package.
稼働中のPHPのバージョンが5.2.14だったので、そのPHPをとってきたリポジトリからyumしました。
yum install --enablerepo=utterramblings php-pear
which peclとコマンドをたたいて、peclが入っているのを確認。
libssh2が必要らしいので、こちらもyumで取ってきます。
yum -y install libssh2 yum -y install libssh2-devel
peclからssh2を入れます。コマンドは、pecl install ssh2を実行。すぐに怒られました。
Failed to download pecl/ssh2 within preferred state "stable", latest release is version 0.11.0, stability "beta", use "channel://pecl.php.net/ssh2-0.11.0" to install install failed
仕方がないので、下記のコマンドを実行。
pecl install channel://pecl.php.net/ssh2-0.11.0
ssh2のインストール中にlibssh2の場所を聞かれます。私の場合、/usrではなかったのでパスを指定しました。
libssh2 prefix? [/usr] : /usr/lib64
しかし、これではうまくいかずエラーが出てしまいました。今日は怒られてばかりです(笑)
ERROR: `/tmp/pear/download/ssh2-0.11.0/configure --with-ssh2=/usr/lib' failed
ググったらlibssh2-develも入れないといけないとわかったので、以下のコマンドを実行します。
yum install libssh2-devel
その後、再度peclコマンドでssh2をインストールします。今度こそうまくいったようなので、php.iniを編集します。
cd /etc/php.d/ # php.iniを設定しますが、私の環境では.iniを分割していたので、適当にzip.iniをコピーして編集しました。 cp zip.ini ssh2.ini vim ssh2.in
extension=ssh2.soに書き換えて保存します。あとは、Apacheを再起動して終わりです。
/etc/init.d/httpd restart
結局うまくいったコマンド
# yum install --enablerepo=utterramblings php-pear # yum -y install libssh2 # yum install libssh2-devel # pecl install channel://pecl.php.net/ssh2-0.11.0 # cd /etc/php.d/ ## php.iniを設定しますが、私の環境では.iniを分割していたので、適当にzip.iniをコピーして編集しました。 # cp zip.ini ssh2.ini # vim ssh2.ini extension=ssh2.soと書き込み # /etc/init.d/httpd restart
ユーザがサマータイムかどうか、そしてサマータイムのオフセットをいくつか、といったデータはユーザがどこに住んでいるか分かっている必要があります。PHP5.2では、タイムゾーンとサマータイムを考慮した、タイムゾーン識別子をいくつも用意してくれています。識別子一覧の取得方法は前回のブログで書きました。識別子の大部分は、地域/都市名という書式になっています。
サマータイムを反映するには、内部的にユーザデータとタイムゾーン識別子を紐付けておく必要があるでしょう。そのため、こうしたシステムでユーザに識別子を選んでもらうインターフェイスが必要になります。今回は、タイムゾーン識別子一覧からセレクトボックスを作る方法を説明しようと思います。
前回の記事に引き続き、PHPのDateTimeZoneクラスを継承したRyus_DateTimeZoneクラスを使って、実装してみようと思います。今回、新しく、Ryus_DateTimeZoneに以下のメソッドを追加しました。識別子は、地域・国都市の順にスラッシュで区切られています。このメソッドは、地域と国都市に分解して、多次元配列にして返すものです。PHP5で非推奨になった修飾子とUTCは除外されます。
public static function getIdentifiersAsArray() { $identifiers = self::listIdentifiers(); $result = array(); foreach ( $identifiers as $identifier ) { if ( $identifier === 'UTC' ) { continue; } $parts = explode('/', $identifier); $area = $parts[0]; $city1 = $parts[1]; if ( isset($parts[2]) ) { $city2 = $parts[2]; $result[$area][$city1.' > '.$city2] = $identifier; } else { $result[$area][$city1] = $identifier; } } return $result; }
そして、下が上のメソッドを使ったセレクトボックスの生成方法です。地域ごとにoptgroupタグでくくったものが生成されます。
<?php require 'Ryus_DateTimeZone.php'; $ret = Ryus_DateTimeZone::getIdentifiersAsArray(); ?> <select name="timezone"> <option value="">--------</option> <? foreach ( $ret as $area => $cities ) :?> <optgroup label="<? echo $area ?>"> <? foreach ( $cities as $city => $identifier ) : ?> <option value="<? echo $identifier ?>"><? echo $city ?></option> <? endforeach ?> </optgroup> <? endforeach ?> </select>
このリストを生成してみるとわかりますが、optgroupで地域別にくくったと言っても選択肢が多すぎて、決してユーザフレンドリーなインターフェイスとは言えないものになります。どのようにしたら、もうすこし使いやすいセレクトになるか考える必要がありそうです。次回につづく...
今回作成したサンプルのダウンロード:
今回JavaScriptネタが思い浮かばなかったので普通のPHPネタです。
個人的にXOOPS管理画面の一般設定で設定完了後に良く分からないページに飛ばされるのがムカついたので、ちょっと弄って元のページにリダイレクトするようにしてみました。
{XOOPS_ROOT_PATH}/modules/legacy/admin/actions/PreferencesEdit.class.php を開いて以下の様に置き換えます。
line 304
$controller->executeForward("./index.php?action=PreferenceList");
include_once XOOPS_ROOT_PATH . "/include/cp_functions.php";
$controller->executeRedirect("./index.php?action=PreferenceEdit&confcat_id=" .xoops_getrequest('confcat_id'), 2, '設定を完了しました');
line 352
$controller->executeForward(XOOPS_MODULE_URL . '/' . $this->_mMaster->mModule->get('dirname') . '/admin/');
include_once XOOPS_ROOT_PATH . "/include/cp_functions.php";
$controller->executeRedirect("./index.php?action=PreferenceEdit&confmod_id=" .xoops_getrequest('confmod_id'), 2, '設定を完了しました');
好みによりますが、これで多少快適にXOOPS使えるようになるんじゃないかなー。