MySQLではインデックスを使用しても30%以上のレコードにアクセスしなければならない時インデックスは使用されない。が、LIMIT句があればインデックスを使用する。
MySQL では利用可能な場合でもインデックスが使用されない場合があることに注意してください。この一例として、インデックスの使用によって、MySQL がテーブルの 30% を超えるレコードにアクセスする必要が生じる場合が挙げられます(この場合は、必要なシークが大幅に減少するため、テーブルスキャンのほうが高速になる可能性が高くなります)。 ただしこのクエリに、レコードの一部のみを取り出す LIMITが使用されている場合、結果で返される少数のレコードを迅速に検索できるため、MySQL はインデックスを使用します。
6.4.5. MySQLにおけるインデックスの使用
例えば’2007-01-01 < register_date < 2007-12-31′な数百万件データがありregister_dateにインデックスを張っているものとして、以下のSQLの条件の日付を進めていくと急にインデックスが効かなくなる。
SELECT * FROM `foo` WHERE `register_date` < '2007-01-01'
知っていればどうという事はないものの何も考えず実装してしまうとデータ量が増えると予想外の時間が掛ってしまう時があるかもしれない。データ量を増やしたテストとexplainは重要。
データ量が増えるなら差分のみを対象に出来る設計にした方が良いと思う。
RailsやCakePHPなどのフレームワークではお馴染みのfindBy系メソッドをZendFrameworkで実装したいということで。
Zend_Db_Tableクラスを継承したMyZend_Db_Tableクラス内のマジックメソッドで実装し、ModelはMyZend_Db_Tableを継承する。
<?php
class MyZend_Db_Table extends Zend_Db_Table {
/**
* 論理削除カラム名
* @var string
*/
private $_deleteCol = 'delete_time';
/**
* findAllBy[ColName]($value [, $flag])でデータアクセス
*
* $flag = true で論理削除された行は除外して取得
*
* @param string $method
* @param array $args
* @return Zend_Db_Table_Row_Abstract
*/
public function __call($method, $args) {
if (preg_match('/^findAllBy(\w+)?$/', $method, $matches)) {
$field = strtolower(preg_replace('/([a-z])([A-Z])/', '$1_$2', $matches[1]));
$where = array();
$where["`$field` = ?"] = $args[0];
if (isset($args[1]) && $args[1] === true
&& in_array($this->_deleteCol, $this->_cols)) {
$where[] = "`{$this->_deleteCol}` IS NULL";
}
return $this->fetchAll($where);
}
throw new Zend_Db_Table_Exception("Call undefine method $method()");
}
}
これで$model->findAllByName(’Design’);のようにアクセスすれば`name` = ‘Design’という条件でのデータアクセスが可能になる。
データは論理削除する事が多いのでfindAllByName(’Design’, true)の形の呼び出しで
`name` = ‘Design’ AND `delete_time` IS NULL
有効な行のみ返すようにしている。
まぁそんな感じで移植実装がおこなわれていくわけです。
Smarty互換でSmartyより軽量なTemplateLiteを使う
に書いた後適度に使っていたところ、Smartyと決定的な差異を発見。
TemplateLiteはオブジェクトにアクセスできない。はじめからオブジェクトをアサインしないような作りにしておけば問題無いものの、既にSmartyでオブジェクトをアサインして使っている場合、テンプレートエンジンをそのまま差し替えるわけにはいかないので注意。
例えば、Zend_Validateのエラー結果を纏めて以下のようなクラスに渡したオブジェクトをアサインしているとする。
<?php
/**
* @package Sample
*/
class Sample_Validate_Result {
/**
* @var array
*/
private $_errors;
/**
* @param array $errors
*/
public function __construct(array $errors) {
$this->_errors = $errors;
}
/**
* エラーの有無を返す
*
* @param string $name
* @return mixed
*/
public function __get($name) {
if (array_search($name, $this->_errors) !== false) {
return true;
} elseif (!array_key_exists($name, $this->_errors)) {
return false;
} else {
return new Sample_Validate_Result($this->_errors[$name]);
}
}
}
その状態でテンプレートからアサインしたオブジェクトのメンバ変数にアクセスしようとTemplateLiteの場合、evalに失敗してエラー。
<?php
// Validatorから帰ってくるエラー例
$errorArray = array(
'nickname' => array('isEmpty'),
'mailaddress' => array('tooLong', 'invalid')
);
// コントローラー内でエラーオブジェクトをアサイン
$this->view->errors = new Sample_Validate_Result($errorsArray);
// Smartyならこれができる
{ if $errors->nickname->isEmpty }
完全互換だと思っていて差し替えが後になってしまうと、影響範囲も広がってしまうので記憶の片隅にでも。
忘れないうちにLinuxでキーバインド変更用の.XmodmapとVimの設定.vimrcを記録。
共にホームディレクトリ以下に配置すればよし。
.Xmodmap
CapslockをCtrlに、無変換キーを全角/半角に。
clear Lock
clear Control
!! 左Ctrl → Super
keycode 37 = Super_L
!! Caps Lock → Ctrl
keycode 66 = Control_L
!! 半角/全角 → Esc
keycode 131 = Zenkaku_Hankaku
add Control = Control_L Control_R
.vimrc
とりあえず切り貼りして使っている感じ。
"--------------------------------------------------------------------------
" Last Change: 2007-11-18
" Maintainer: Design
"
"- Memo -------------------------------------------------------------------
"
" :set number? オプションの設定状況を表示
" :set all 全てのオプションの設定状況を表示
" :set デフォルトから変更されたオプションの設定状況を表示
" :mkvimrc 現在のオプションを ~/.vimrc に書き出す(存在すれば無効)
" :source ~/.vimrc 設定ファイルを読み直す
"--------------------------------------------------------------------------
" Global
set nocompatible
syntax on
colorscheme desert
"set number
set nobackup
set autoindent
"set smartindent
" UTF-8, SJIS, EUCの順番に開く
set fileencodings=utf-8,cp932,euc-jp
" デフォルトはUTF-8
set fileencoding=utf-8
"--------------------------------------------------------------------------"
" Edit
" タブ幅
set tabstop=4
" タブの代わりに空白
set expandtab
" バックスペースでインデントや改行を削除できるようにする
set backspace=2
" 括弧入力時に対応する括弧を表示
set showmatch
"カーソルを行頭、行末で止まらないようにする
"set whichwrap=b,s,h,l,<,>,[,]
"全角スペースを視覚化
highlight ZenkakuSpace cterm=underline ctermfg=red guibg=lightgray
au BufNewFile,BufRead * match ZenkakuSpace / /
"--------------------------------------------------------------------------
" Status bar
" 常にステータスバーを表示
set laststatus=2
" コマンドラインの高さ
set cmdheight=1
" コマンドを表示
set showcmd
" 文字コード・改行コードを表示
set statusline=%<%f\
%m%r%h%w%{'['.(&fenc!=''?&fenc:&enc).']['.&ff.']'}%=%l,%c%V%8P "
" カーソル位置情報を表示
set ruler
" コマンドライン補完するときに強化されたものを使う
set wildmenu
"--------------------------------------------------------------------------
" Search
" サーチをインクリメンタルサーチにする
set incsearch
" 検索文字列が小文字の場合は大文字小文字を区別なく検索する
set ignorecase
" 検索文字列に大文字が含まれている場合は区別して検索する
set smartcase
" 検索時にファイルの最後まで行ったら最初に戻る
set wrapscan
" 検索結果文字列のハイライト
set hlsearch
"--------------------------------------------------------------------------
" Buffer
"変更中のファイルでも保存せず他のファイルを表示
set hidden
ウノウラボ Unoh Labs: PHPで暗号化・復号あれこれ
に暗号化の話が出ていたので、追記する形で他にいくつか。
同じ記事を書いていた前のBlogが消えてしまったので流し書き程度に。
gnupg 関数
PHPからOpenPGPであるgnupg 関数を利用して鍵方式の暗号化がおこなえます。
gnupg関数を使用する為にPECLのgnupgモジュールが必要。
速度的にも以降の方法に比べて早い。PGP暗号化では一番最初に検討したい。
PEAR::Crypt_RSA
PEAR::Crypt_RSAパッケージで公開鍵暗号がおこなえる。
GMP, BigInt, BCMathの3種類が使え、速度は(GMP>BigInt>BCMath)でGMPを基準として(1倍>5倍>250倍)遅い。暗号化するものが少なければ問題無いものの、多くなると速度以外でも色々と問題がある。
動作環境はDebian / PHP5.2.0 / Crypt_RSA 1.2.0bで確認。
- GMPを使うと暗号化時にメモリリーク(PHP自体)、復号は大丈夫。
- BigIngを64bit環境で使うと暗号化できない(空文字になる)。
PEAR::Crypt_RSAを使うしか選択肢が無い状態で、3000レコード(1レコード1M)ほどのデータを各行暗号化してDBに入れ、しばらくして復元した時、テスト環境が32bitの本番環境が64bitで泣いた記憶があるので(結局分割してバックグラウンドでGMPで暗号化した)、そもそも環境が違うなんて・・・というのはおいておき、もしかしたら特有の問題かもしれませんが似たような環境で使う人は注意。
exec()でgpgコマンドを叩く
http://www.alt-php-faq.org/local/65/
もしかすると幾多もの外的要因によりこういった方法を選択せざるを得ない時もあるかもしれない。GnuPGが必要なのでインストールしておく。
とても重いので頻度が少ないならまだしも、多くなると実用には耐え切れない。既にGnuPGが入っている状態なら何も必要無いのでそういう点で利用は出来るかもしれない。