btree index (24)
ユニークインデックスについて
ユニークインデックスにアイテムを登録するとき、_bt_check_uniqueでユニークをチェックしている。このとき登録するアイテムと同じキーを持つアイテムが見付かって、それが削除可能である場合(実行中の全てのトランザクションで不要)、アイテムにLP_DELETEフラグを設定している。
else if (htup.t_data != NULL) { /* * Hmm, if we can't see the tuple, maybe it can be * marked killed. This logic should match * index_getnext and btgettuple. */ LockBuffer(hbuffer, BUFFER_LOCK_SHARE); if (HeapTupleSatisfiesVacuum(htup.t_data, RecentGlobalXmin, hbuffer) == HEAPTUPLE_DEAD) { curitemid->lp_flags |= LP_DELETE; SetBufferCommitInfoNeedsSave(buf); } LockBuffer(hbuffer, BUFFER_LOCK_UNLOCK); }
この削除マークをつけるアイテムが再利用できないか?
このアイテムには登録しようとしているアイテムと同じデータが書き込まれているので、新しいアイテムを登録する代わりに、削除されたアイテムを復活させてTIDを変更するだけで登録したことにならないか?これが可能なら、更新が集中するアイテムでbtree indexが大きくなるのを少しは抑えることができる。
(追記)
TIDを変更するだけではダメ。_bt_equalなどの比較関数が二つのアイテムが同じだと判定したとしても、これらのアイテムのビットパターンが同じとは限らない。例えばIEEEの浮動小数点型では、+0と-0は比較すれば同じだがビット表現は異なる。
アイテム全体を上書きすればOKか?
またページのWrite lockを保持していても、削除されたアイテムを上書きして良いわけではない。同時実行中のトランザクションがそのページを検索中の場合、正常に動作しなくなる可能性がある。
btree indexではページを検索中にRead lockを保持したままではなく、アイテムを取り出してexecutorに返すときは一旦Read lockを開放している。executorから次のアイテムを取得する要求があったときにRead lockを再度取得して検索を再開する。ただしRead lockは開放するが、まだこのページに興味があることを他のトランザクションに伝えるために、ページの参照カウンタは減らさない。
もしWrite lockを取得して参照カウンタが1であれば(super-exclusive lock)、自分しかこのページを参照していないことが保証されるので、アイテムを上書きできるはず。参照カウンタが2以上ならば、他のトランザクションがこのページを検索中なので、アイテムを上書きするのは安全ではない。