mysqlのバグ?ORDER BY DESCが無効になる

5.1.23-rc で発生。

SELECT * FROM entries WHERE blog_id = 1 ORDER BY point DESC LIMIT 2000;

みたいなクエリで ORDER BY point DESC が無視されるという現象が発生(blog_id, point はインデックスを張ってある)。
無視されるというか、ORDER BY id DESC になっていた(id は PRIMARY KEY)。
前日までは正常に並び替えもされていて突然順番が変になった。
100万レコードを超えたりしてたんだけど、このあたり関係あるかな?
十分な検証ができておりませんが、とりあえず、

SELECT * FROM entries WHERE blog_id = 1 ORDER BY point DESC, id LIMIT 2000;

って書くと正常に動作します(ORDER BY に id を追加した)。
parser あたりのバグでしょうか?

railsでのパーティショニング使用

パーティショニングそのものについてはmysql5.1のパーティショニングを使う - d2mrの日記参照

rails経由でパーティショニングの追加をmigrationファイルに以下のように記述する

class AddPartition < ActiveRecord::Migration
  def self.up
    # パーティショニングに使う値がプライマリキーでないと怒られるので複合キーに変更する
    execute("ALTER TABLE entries DROP PRIMARY KEY, ADD PRIMARY KEY (id, updated_at);")

    # パーティショニングの追加
    execute("ALTER TABLE entries PARTITION BY RANGE (TO_DAYS(updated_at)) (PARTITION p200803 VALUES LESS THAN (TO_DAYS('2008-04-01')), PARTITION p200804 VALUES LESS THAN (TO_DAYS('2008-05-01')));")
  end

  def self.down
    # パーティショニングの取り消し
    execute("ALTER TABLE entries REMOVE PARTITIONING;")
    # id プライマリキーに戻す
    execute("ALTER TABLE entries DROP PRIMARY KEY, ADD PRIMARY KEY (id);")
  end
end

複合主キー(composite primary keys)を扱うことになるが、
実質的な主キーはidのみなのでアプリ側を変更する必要はありません

パーティショニングを使うためのmysql5.1インストール

ソースからインストールする場合は以下のような感じでオプションに --with-partition ってしておく。
バイナリをインストールする場合には気にしなくてもOK。

# ./configure --prefix=/usr/local/mysql --with-charset=utf8 \
  --with-extra-charsets=all --with-mysqld-user=mysql --with-innodb --with-partition

mysql5.1のパーティショニングを使う

mysql5.1の新機能であるパーティショニング(PARTITIONING)を使ってみたので使い方やら注意点やらまとめ。

最初にパーティションの区切りの基準になるカラムはプライマリキーじゃないといけないという制約があるのでプライマリキーを変更する。

ALTER TABLE entries DROP PRIMARY KEY, ADD PRIMARY KEY (id, blog_id);

実際にパーティションに分割する

ピンポイント数値で分割
ALTER TABLE entries PARTITION BY LIST (blog_id) (
  PARTITION p1 VALUES IN (1,3,7),
  PARTITION p2 VALUES IN (10,11)
);
数値範囲で分割
ALTER TABLE entries PARTITION BY RANGE (blog_id) (
  PARTITION p1 VALUES LESS THAN 3,
  PARTITION p2 VALUES LESS THAN 10,
  PARTITION p_max VALUES LESS THAN MAXVALUE
);
日付(DATE, DATETIME)の年で分割
ALTER TABLE entries PARTITION BY LIST (YEAR(updated_at)) (
  PARTITION p2007 VALUES IN (2007),
  PARTITION p2008 VALUES IN (2008)
);
日付(DATE, DATETIME)の月で分割
ALTER TABLE entries PARTITION BY RANGE (TO_DAYS(updated_at)) (
  PARTITION p200804 VALUES LESS THAN (TO_DAYS('2008-05-01')),
  PARTITION p200805 VALUES LESS THAN (TO_DAYS('2008-06-01')),
  PARTITION p200806 VALUES LESS THAN (TO_DAYS('2008-07-01'))
);
パーティションを追加する
ALTER TABLE entries ADD PARTITION (PARTITION p3 VALUES LESS THAN 100));

注意:RANGE分割でMAXVALUEを使っている場合に、ADD PARTITION をするとそのテーブルの操作が一切できなくなります。(5.1.23-rc で確認)

パーティションを削除する
ALTER TABLE entries DROP PARTITION p3;

注意:DROPするパーティションに入っているデータが全部消えます。

パーティショニング(分割)をやめる
ALTER TABLE entries REMOVE PARTITIONING;

よくやる意味のないコード

自戒の意味をこめて。

entry = Entry.find(params[:id])
return false unless entry

実際には entry が見つからなかったら
ActiveRecord::NotFound
の例外が起こるので entry が nil になって次の行が実行されることは実はない。
ちなみに、引き数に配列をいれた場合は、
引き数に入っているidのレコードが全部見つからないと例外になる。

解決策は

begin
  entry = Entry.find(params[:id])
rescue ActiveRecord::NotFound
  return false
end

entry = Entry.find_by_id(params[:id])
return false unless entry

あたりかな。他にもあると思いますが。

CapistranoでWindowsから踏み台サーバを経由して公開鍵認証でデプロイ

Capistranoでいろいろ詰まったのメモしておきます。
基本的な使い方はとりあえず他に任せる方針で。。。

踏み台サーバを経由する

set :gateway, "gateway.nanika.jp"
set :ssh_options, :forward_agent => true, :port => 12345

SSHポートを変更する

role :app, "192.168.1.1", :port => 12345

db:migrateする環境を変更する(デフォルトは production)

set :rails_env, "development"

sudo を利用しなくする

set :use_sudo, false

Windowsから公開鍵認証でデプロイするには、putty の pagent に秘密鍵を登録しておけばよい。