2010-11-23 29 views
0

〜100Kページ(200〜300人の同時ユーザー)を含むWebサイトがあります。各ページは、MySQLのInnoDBテーブルで彼自身の記録を持っている - ページ:あなたは私がなどなど、ページの階層を表示するには、ネストされたセットモデルを使用して見ることができるようにネストセットモデルの最適化

page_id 
page_parent 
left_id 
right_id 
page_subject 
page_children 
page_depth 
.... 

...すべてがINSERTのパフォーマンスを除いて罰金のようです、UPDATEおよびDELETEステートメント。ページを1つの親から別のものに移動するのが非常に遅く、時には〜30秒かかることがあります(!!)

CREATE PROCEDURE `PAGE_MOVE`(IN `pageSrc` INT, IN `pageDst` INT) 
    LANGUAGE SQL 
    NOT DETERMINISTIC 
    CONTAINS SQL 
    SQL SECURITY DEFINER 
    COMMENT '' 
BEGIN 
    DECLARE srcLeftId INT; 
    DECLARE srcRightId INT; 
    DECLARE dstLeftId INT; 
    DECLARE dstRightId INT; 
    DECLARE width INT; 

    SELECT left_id, right_id, right_id - left_id + 1 
    INTO srcLeftId, srcRightId, width 
    FROM page 
    WHERE page_id = pageSrc; 

    IF pageDst > 0 THEN 
     SELECT left_id, right_id 
     INTO dstLeftId, dstRightId 
     FROM page 
     WHERE page_id = pageDst; 
    ELSE 
     SELECT MAX(right_id) INTO dstLeftId FROM page WHERE page_parent = 0; 
     SET dstRightId = dstLeftId + 1; 
    END IF; 

    IF dstLeftId > 0 THEN 

     UPDATE page SET page_children = page_children - (width/2) 
     WHERE left_id < srcLeftId AND right_id > srcRightId; 

     UPDATE page SET page_children = page_children + (width/2) 
     WHERE left_id <= dstLeftId AND right_id >= dstRightId; 

     /** 
     * Set nagative values to left_id and right_id (temporary) 
     */ 
     UPDATE page 
     SET left_id = -left_id, right_id = -right_id 
     WHERE left_id >= srcLeftId AND right_id <= srcRightId; 

     UPDATE page SET left_id = left_id - width WHERE left_id > srcLeftId; 
     UPDATE page SET right_id = right_id - width WHERE right_id > srcRightId; 

     UPDATE page SET left_id = left_id + width 
     WHERE left_id >= IF(dstRightId > srcRightId, dstRightId - width, dstRightId); 

     UPDATE page SET right_id = right_id + width 
     WHERE right_id >= IF(dstRightId > srcRightId, dstRightId - width, dstRightId); 

     SET @diff = IF(dstRightId > srcRightId, dstRightId - srcRightId -1, dstRightId - srcRightId - 1 + width); 

     UPDATE page 
     SET left_id = -left_id + @diff, 
      right_id = -right_id + @diff 
     WHERE left_id <= -srcLeftId AND right_id >= -srcRightId; 
     /** 
     * Set parent_id and page_depth 
     */ 
     UPDATE page SET page_parent = pageDst, page_depth = GET_PAGE_DEPTH(page_id) WHERE page_id = pageSrc; 

     /** 
     * Update page_depth in children's nodes 
     */ 
     IF width > 2 THEN 

      SELECT left_id, right_id 
      INTO srcLeftId, srcRightId 
      FROM page 
      WHERE page_id = pageSrc; 

      UPDATE page 
      SET page_depth = GET_PAGE_DEPTH(page_id) 
      WHERE left_id >= srcLeftId AND right_id <= srcRightId;  
     END IF; 


    END IF; 
END 

この手順を最適化するにはどうすればよいですか?または疑問があります:ネストされたセットモデルを使用する代わりに何がありますか?

機能GET_PAGE_DEPTH():

CREATE FUNCTION `GET_PAGE_DEPTH`(`pageId` MEDIUMINT UNSIGNED) 
    RETURNS smallint(6) 
    LANGUAGE SQL 
    DETERMINISTIC 
    READS SQL DATA 
    SQL SECURITY DEFINER 
    COMMENT '' 
BEGIN 
    RETURN (
     SELECT COUNT(*) -1 AS depth 
     FROM page AS parent 
     INNER JOIN page AS node ON node.page_id = pageId 
     WHERE node.left_id BETWEEN parent.left_id AND parent.right_id 
    ); 
END 

ハードウェア:クアッドコアQ6600(4X 2.40+ギガヘルツ)、4ギガバイトのRAM何かアドバイスのため

ありがとう!

EDITED:

表ページは次のようになります。最初のステップとして

CREATE TABLE `page` (
    `page_id` int(10) unsigned NOT NULL AUTO_INCREMENT, 
    `page_parent` int(10) unsigned DEFAULT NULL, 
    `left_id` int(11) NOT NULL, 
    `right_id` int(11) NOT NULL, 
    `page_module` smallint(5) unsigned NOT NULL, 
    `page_connector` smallint(5) unsigned NOT NULL, 
    `page_subject` varchar(255) NOT NULL, 
    `page_title` varchar(255) DEFAULT NULL, 
    `page_path` varchar(255) NOT NULL, 
    `page_text` int(10) unsigned DEFAULT NULL, 
    `page_children` mediumint(8) unsigned NOT NULL, 
    `page_depth` smallint(5) unsigned NOT NULL DEFAULT '0', 
    `page_content` tinyint(3) unsigned NOT NULL, 
    `page_publish` tinyint(1) unsigned NOT NULL DEFAULT '1', 
    `page_published` datetime DEFAULT NULL, 
    `page_unpublished` datetime DEFAULT NULL, 
    `page_time` int(10) unsigned NOT NULL, 
    `page_edit_time` int(10) unsigned NOT NULL, 
    `page_delete` tinyint(1) unsigned NOT NULL DEFAULT '0', 
    `page_richtext` tinyint(1) unsigned NOT NULL DEFAULT '0', 
    `page_cache` tinyint(1) unsigned NOT NULL DEFAULT '1', 
    `page_template` varchar(255) NOT NULL, 
    PRIMARY KEY (`page_id`), 
    KEY `page_parent` (`page_parent`), 
    KEY `left_id` (`left_id`), 
    KEY `right_id` (`right_id`), 
    KEY `page_depth` (`page_depth`), 
    KEY `page_path` (`page_path`), 
    KEY `page_connector` (`page_connector`), 
    KEY `page_text` (`page_text`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8; 
+0

テーブルの構造を投稿すると、特にインデックスが役立ちます。 – Jaydee

+0

ああ、ネストされたセットの喜び - おそらく物事は良い古い隣接リストでもっとシンプルだろうか? –

+0

テーブルの全構造を追加しました。おそらく、ページをあるノードから別のノードに移動する別の方法がありますか? – Bald

答えて

2

あなたは

page_idの

page_parent

left_idのための別々のインデックスを持っていることを確認し、 right_id

ページWHERE page_parent = 0〜dstLeftId INTO

EDIT

SELECT MAX(right_id)をright_id。

上記のクエリは、それ以外

は、私が本当にシステムのはるかに大きい概要を有する可能性が道にそれを変更することなく、あまりお勧めできません有し、かつ(page_parent、right_id)のインデックスによって高速化される可能性があります働く

+0

私は以下のインデックスを持っています:page_id、page_parent、left_id、right_id。私は(left_id、right_id)とright_idのインデックスを分離する必要がありますか? – Bald

+0

「WHERE left_id <= -srcLeftId AND right_id> = -srcRightId;」という表現がいくつかあります。だから私はあなたが2つの別々のインデックスとして必要(left_id、right_id)とright_idと言うだろう。個々のクエリを実行してみると、特に遅いクエリがあるかどうか試してみましたか? – Jaydee

+0

大量のデータを更新する必要がある場合、テーブル "page"のすべてのUPDATE文は遅く、MySQLはテーブルインデックスを更新する必要があります:/とにかく、これはネストされたセットモデルの欠点です:(新しいいくつかのノードの子として記録する。 – Bald