Recherche Fulltext et InnoDB avec Mysql
Par Yves Tannier le lundi, septembre 10 2007, 11:22 - Le codage - Lien permanent
Les avantages de l'utilisation du moteur de stockage InnoDB par rapport à MysIsam sur une base de données Mysql (à partir de la version 5.02) sont non négligeables sur certains projets. Il permet l'utilisation :
- de contraintes d'intégrité (Foreign Key ect...)
- de triggers (déclencheurs en français dans le texte).
- de procédures stockées
- ...
Un inconvénient est, par contre, particulièrement pénalisant dès qu'on envisage un moteur de recherche en "texte intégral". Il est en effet impossible de créer des index FULLTEXT avec le moteur InnoDB ! L'utilisation des MATCH et autres joyeusetés est donc impossible. Le classement pertinent des résultats devient plus compliqué à mettre en oeuvre.
Je me suis retrouvé confronté à ce problème récemment et voici comment je l'ai contourné. Je résume, pour l'exemple, en une unique table, ma structure de données qui en compte quelques dizaines.
J'ai donc une table "texts" qui utilise InnoDB avec quelques champs dont le "title" sur lequel je souhaite faire les recherches (la clé primaire s'appelle "idtext").
J'ai créé une table "searchs_texts" avec le moteur MyIsam, deux champs - idtext/title - et un index FULLTEXT sur le champ "title".
Cette table contient, vous l'aurez compris, l'exacte correspondance des champs idtext/title de la table texts.
Reste à tenir à jour cette table sans modifier le code de tous le site. Les triggers vont nous être bien utile pour ça :
Un pour l'INSERT :
CREATE TRIGGER trigger_insert_texts AFTER INSERT ON texts FOR EACH ROW INSERT INTO searchs_texts SET idtext=NEW.idtext, title=NEW.title;
Un pour l'UPDATE :
CREATE TRIGGER trigger_update_texts AFTER UPDATE ON texts FOR EACH ROW UPDATE searchs_texts SET title=NEW.title WHERE idtext=OLD.idtext;
Et enfin, un pour le DELETE :
CREATE TRIGGER trigger_delete_texts AFTER DELETE ON texts FOR EACH ROW DELETE FROM searchs_texts WHERE idtext=OLD.idtext;
Et voilà. Le tour est joué ! Maintenant, votre moteur de recherche pourra utiliser tous ce qui est disponible et bientôt concurrencer Google 

Commentaires
Franchement pas mal comme astuce. Je viens de mettre ça en place et je dois dire que je suis assez satisfait du résultat... malgré le coté un peu bourin de la chose
Merci. Sur que c'est un peu bourrin mais ça fonctionne correctement en production
Salut,
merci pour l'astuce des triggers avec fulltext
cependant les procédures stockés et les triggers n'ont rien à voir avec le type de moteur de la table.
++
Super !
Bonjour,
Merci pour l'astuce.
Cependant ce système, en l'état actuel, à ses limites : si je "TRUNCATE" la table texts, la table searchs_texts n'est pas mit à jour, Idem si un enregistrement de la table texts est supprimé suite à la suppression d'un élément parent (A cause de FOREIGN KEY)
A moins que l'on mette aussi des déclencheurs sur TRUNCATE et ... ?
Si c'est possible ?
Salut, j'ai fait une banque de Curriculum Vitae en ligne en asp.net et j'ai utilisé une BD MySQL. J'ai utilisé la technique mentionnée plus haut et ça fonctionne vraiment bien! Merci!
Essayez la recheche full text, très rapide : CV en ligne
Bonjour,
J'ai tout bien fait comme indiqué ci-dessus.
Le résultat de mon explain donne
@@id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE clients_full_text ALL NULL NULL NULL NULL 344925 Using where@@
Alors que j'ai créé un index fulltext, il semble ne pas l'utiliser.
Voici ma requete
@@SELECT raison_sociale, nom, prenom
FROM clients_full_text
WHERE nom LIKE '%dupont%'@@
Merci pour vos eclaircissements
Ben la requête change bien sûr
SELECT raison_sociale, nom, prenom
FROM clients_full_text
WHERE MATCH(raison_sociale, nom, prenom) AGAINST ('dupont')
Sachant que l'index fulltext porte sur les champ raison_sociale, nom, prenom
Attention, il faut que le MATCH soit sur l'intégratilité des champs de l'index fulltext
Exact, il faut utiliser MATCH pour une recherche fulltext
Bonjour,
je trouve très intéressant cet article et je pense mettre en application ces conseils
cependant une question me turlipine :
qu'en est il des lock table lors des insert/update/delete des trigger de la table innodb
vers la table myisam pendant qu'un SELECT -> MATCH est fait sur la table myisam ?
En effet j'ai pas mal de trafic sur mon site et mon gros soucis c'est justement les mises à jour des ROWS pendants qu'un SELECT sur un FULLTEXT est fait, ce qui implique des locktables en chaines....
bien à vous
Amba
Honnêtement, je n'en sais rien mais je dirais que le trigger étant "en théorie" géré de manière transactionnel, ça ne devrait pas poser de pb vu que le LOCK TABLES n'intervient pas dans la transaction. Vous dites que le match sur l'index fulltext pose des soucis si il y a un LOCK ? Merci de me tenir informé de vos tests
Bonjour,
Je trouve ce billet très pertinent et il devrait m'être très utile.
Par contre, n'étant pas familier avec les triggers, et vu ce qui est écrit plus haut.
J'ai plusieurs tables avec des dizaines de milliers de lignes déjà enregistrées.
Comment faire pour les récupérer ? Un simple INSERT ... SELECT ?
Merci
Cette strategie est utile pour un site faible/moyen traffic.
Cela dit, si la motivation a passer en innodb etait de beneficier du passage du "table lock" au "row lock", il serait sage de faire des tests...
Sinon voila une liste de possibilites:
http://www.mysqlperformanceblog.com...
Plus simplement, il a aussi une methode tres efficace pour augmenter les performances sans trop se creuser la tete:
http://www.ssdperformanceblog.com/
Je n'utilise pas InnoDB pour la beauté du geste et le site fait 2 millions de pages vues par mois
Merci pour le lien ssdperformance. Je vais jeter un oeil même si je suis déjà convaincu que le SSD améliore grandement les performances des bases de données.
@Neiyo avec quelques mois de retard > oui, un INSERT SELECT suffit