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以上ならば、他のトランザクションがこのページを検索中なので、アイテムを上書きするのは安全ではない。