2010-11-23 18 views
0

J'ai un site Web qui contient ~ 100K pages (200-300 utilisateurs simultanés). Chaque page a son propre enregistrement dans la table mysql InnoDB - Page:Optimisation du modèle de jeu imbriqué

page_id 
page_parent 
left_id 
right_id 
page_subject 
page_children 
page_depth 
.... 

Comme vous pouvez le voir, j'utiliser le modèle de jeu imbriqué pour afficher la hiérarchie des pages, etc, etc .. Tout semble être bien sauf exécution de INSERT , UPDATE et DELETE. Le déplacement de la page d'un parent à l'autre est extrêmement lent et prend parfois ~ 30 sec (!!)

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 

Comment optimiser cette procédure? Ou peut-être question devrait être: Quelle est l'alternative d'utiliser le modèle de jeu imbriqué?

Fonction 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 

Matériel: Quad Core Q6600 (4x 2.40+ GHz), 4 Go de RAM

Merci pour tout conseil!

ÉDITÉE:

tableau page ressemble à ceci:

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

Pouvez-vous poster la structure de la table, il aidera, en particulier les index. – Jaydee

+0

ahhh, les joies des ensembles imbriqués - peut-être que les choses auraient pu être plus simples avec la bonne liste d'adjacence olde ?? –

+0

J'ai ajouté la structure complète de la table. Peut-être qu'il y a une manière différente de déplacer la page d'un noeud à l'autre? – Bald

Répondre

2

En premier lieu vous assurer que vous avez des index séparés pour

page_id

page_parent

left_id, right_id

right_id

EDIT

SELECT MAX (right_id) DANS LE CAS DE LA PAGE dstLeftId page_parent = 0;

La requête ci-dessus peut être accélérée par avoir et index sur (page_parent, right_id)

Autre que je ne peux pas penser vraiment beaucoup sans avoir une vue d'ensemble beaucoup plus du système et peut-être modifier la façon dont il travaux.

+0

J'ai les index suivants: page_id, page_parent, left_id, right_id. Dois-je séparer les index sur (left_id, right_id) et right_id? – Bald

+0

Vous avez plusieurs occurrences de "WHERE left_id <= -srcLeftId AND right_id> = -srcRightId;" Donc je dirais que oui vous avez besoin (left_id, right_id) et right_id comme deux index séparés. Avez-vous essayé d'exécuter les requêtes individuelles pour voir lesquelles, le cas échéant, sont particulièrement lentes? – Jaydee

+0

Chaque instruction UPDATE sur la table "page" est lente quand nous devons mettre à jour une grande quantité de données, et MySQL doit mettre à jour leur index de table:/De toute façon, ceci est un inconvénient du modèle imbriqué :(Fonctionne bien jusqu'à ce que enregistrer en tant qu'enfant d'un noeud. – Bald