Journal Aujourd'hui, petit bug d'un serveur MySql d'OVH

Posté par .
Tags : aucun
3
20
avr.
2012

On m'appelle ce matin pour me dire que le planning en ligne bug.
La première ligne d'une plage horaire reste invariablement vide. Je n'ai pas touché à cette partie code depuis plusieurs mois
ce qui me donnait la certitude que çà marchait avant. De plus , l'application est utilisé quotidiennement par plus d'une dizaine d'utilisateur.

Après enquête, je constate que les données en question se trouve bien dans la base.

La problème vient "d'un supérieur ou égale" dans une requête sur un timestamp qui a maintenant systématiquement le comportement "d'un supérieur stricte".
Si je fais la requête en soustrayant une minute, ma donnée manquante apparaît !
Lorsque j'importe la base de prod sur mon environnement de dev , "le supérieur ou égale" a toujours un comportement normal.

Je remarque alors que j'ai un index sur cette table. Lorsque je supprime l'index, "le supérieur ou égale" a de nouveau un comportement normal si on omet la lenteur du système.
Si je recrée cette index. le comportement de l'opérateur est de nouveau strict

Sur OVH Travaux, je peux constater qu'il ont fait une mise à jour des serveurs MySQL le 19/04 … comment s'occuper un vendredi :).

  • # Le bug c'est la requete

    Posté par . Évalué à 4.

    Le bug c'est ta requete: On n'utilise pas >= pour des dates, tu casses l'optimisation de l'index.

    -> WHERE stamp BETWEEN x AND x

    • [^] # Re: Le bug c'est la requete

      Posté par . Évalué à 6.

      Je suis curieux de connaître la raison ([url] vers la documentation MySQL par exemple) expliquant comment le système est capable d'effectuer & d'optimiser plus rapidement deux comparaisons plutôt qu'une seule ! Ça ressemble presque à de la magie noire de couleur !

      « Je vous présente les moines Shaolin : ils recherchent la Tranquillité de l'Esprit et la Paix de l'Âme à travers le Meurtre à Main Nue »

      • [^] # Re: Le bug c'est la requete

        Posté par . Évalué à 4.

        Sources ? De mémoire bouquin High Performance MySQL, et la doc Mysql et sur les partitions.

        Il n'y a pas deux comparaisons. Il faut penser qu'on est sur un index. (la remarque serait valide si ce n'était pas indexé)
        Avec un between tu permets à l'optimiseur de se fixer des bornes, et donc d'aller uniquement chercher les éléments dans l'index correspondant. Si t'as des partitions dans ta table, sans le between il ne saura pas les exploiter.

        C'est à peu près pareil que la différence entre un WHERE a IN(x, y) et WHERE (a=x OR a=y).

        • [^] # Re: Le bug c'est la requete

          Posté par (page perso) . Évalué à 4. Dernière modification le 20/04/12 à 22:49.

          Mouais pas d'accord avec ça, la principale différence ici c'est très probablement une migration vers MySQL 5.1, qui change le comportement de MySQL lors d'une comparaison DATE vs DATETIME.

          Avant la 5.1, dans le cas d'une comparaison DATE vs DATETIME MySQL castait le tout en DATE avant d'effectuer le test ; maintenant il caste en DATETIME.

          En gros :
          avant la 5.1 : '2012-04-20' = '2012-04-20 18:42:00'
          après la 5.1 : '2012-04-20' = '2012-04-20 00:00:00' < '2012-04-20 18:42:00'

          Une solution est le between, mais il suffit de faire un EXPLAIN pour voir que MySQL utilise parfaitement l'index dans tous les cas.

          Par contre ce qu'on peut retrouver dans le bouquin en question ce sont des choses comme ça :
          1) WHERE monChamp + 1 interval 1 month 2) WHERE monchamp

          La première construction ne saura pas tirer parti d'un éventuel indexe, au contraire de la seconde.

          alf.life

          • [^] # Re: Le bug c'est la requete

            Posté par (page perso) . Évalué à 2. Dernière modification le 21/04/12 à 00:18.

            erf, alors :

            1. je découvre le "parseur" de LinuxFr, qui ne gère pas les "<", faut se coltiner le HTML, chouette. Donc mes exemples étaient :

            1) WHERE monChamp + interval 1 month < curdate()

            2) WHERE monChamp < curdate() - interval 1 month

            J'aurais du relire, dsl.

            De plus je me suis fourvoyé en prétendant que MySQL utilisait l'index dans tous les cas : puis qu'il ne peut le faire sur les résultats de calculs / conversion comme ci dessus, je ne vois pas comment il aurait pu dans le cas d'un MySQL 5.0 convertissant la requête "WHERE to_date(stamp) = X"

            alf.life

            • [^] # Re: Le bug c'est la requete

              Posté par . Évalué à 2.

              Tout d'abord, merci à tous pour ces commentaires constructifs. Je ne connaissais pas cette notion dans l'optimisation des requêtes.

              Je viens de tester ma requete :
              select * from RendezVous where (jour between '2012-05-03 08:00:00.0' and '2012-05-03
              17:20:00.0') order by jour
              ou
              select * from RendezVous where jour >= '2012-05-03 08:00:00.0' and jour <= '2012-05-03 17:20:00.0' order by day

              J'ai exactement le même comportement quand j'active l'index. Les RendezVous a 2012-05-03 08:00:00.0 ne sont pas inclus alors qu'ils le sont dans mon environnement dev.
              Et ils apparaissent dans les deux cas,si je supprime l'index.

              Enfin, un explain sur les deux requetes montre qu'ils selectionnent toutes les deux le même nombre de lignes quand l'index est présent.
              Enfin, quand ils n'existent pas cet index, l'explain m'indique qu'il selectionne l'ensemble des lignes de la table.

              • [^] # Re: Le bug c'est la requete

                Posté par . Évalué à 2.

                C'est un peu long à expliquer, et en plus je ne suis pas forcément au top sur les dernières versions de mysql.
                Grosso-modo :

                a) Mysql affiche et stoque des données à la seconde près, mais bosse en interne sur des microsecondes.
                b) Sauf indications contraires la première colonne d'une table au format timestamp est forcément auto-initialisée/auto-updatée. Si vous empéchez ceci, la colonne suivante au format timestamp récolte de l'auto initialisation/auto-update par la suite etc.
                CF https://linuxfr.org/nodes/89781/comments/1327695 (oui c'est pas beau de s'auter citer, mais là c'est vraiment pile poil le truc qui explique et on gagne du temps)
                c) La valeur 0 dans un timestamp est très particulière, par défaut elle est interdite (donc renvoit un résultat faux quoi qu'il arrive) sauf si on autorise les dates à 0.
                d) Par défaut toujours un timestamp ne peut pas être null.

                Il est bon de lire aussi le mode de comparaison de timestamp chez mysql, qui vaut le détour : http://dev.mysql.com/doc/refman/5.0/en/using-date.html

                A partir de là il peut se passer pleins de choses suivant le connecteur que tu utilises :

                1- Quand il n'y a pas d'index, la comparaison se fait sur des entiers longs (normalement). Mais quand il y a un index pour une raison x,y ou z la comparaison se fait sur des strings. Et là tout peut se produire. En fait le problème peut même venir de ton .0 qui n'existera pas dans ton format de base. Et en chaine on a bien '2012-05-03 08:00:00.0' Essaye de gicler le .0 et regarde ce qu'il se passe. C'est l'hypothèse la plus probable.

                2- Autre possibilité, OVH a modifié le format de stockage des timestamp (comme il est possible de le faire depuis la 5.6.4 et peut être sur les versions précédentes avec les backports), et comme tu demandes explicitement les dizièmes de secondes il te stocke en décimal (ou pire en flotant). A l'affichage ca ne se voit pas car MySQL continue a ne prendre en compte que l'arrondi à la décimale supérieure, mais à la comparaison ca fache. Il manque quelques dizièmes de secondes pour que ca passe.
                Pour valider il suffit de faire un select de la date avec un cast sur un décimal et de regarder sil y a bien que des 0 dans la partie décimale. Moins probable mais possible.

                3- Ton connecteur interprête la requète différamment suivant qu'il s'agisse d'une colonne avec index ou non, et quand il y a un index il décide de forcer la comparaison directement sur le type timestamp - et là c'est le drame, la comparaison renvoit un timestamp à 0, qui est interdit => comparaison forcément fausse. Très improbable, csi c'est le cas il y a un gros bug dans le connecteur.

                • [^] # Re: Le bug c'est la requete

                  Posté par . Évalué à 1.

                  Je viens de faire un test en supprimant le ".0". L'index est bien pris en compte et j'obtiens alors le bon résultat !
                  Cela signifierai que cette nouvelle version MySQL recherche dans l'index sous la forme d'une string alors que lorsqu'il fait un full scan , il traite la date sous la forme d'un decimal.

                  Sinon, j'ai essayé de faire les cast.
                  En DECIMAL , J'ai un nombre identique quelques soit la date 999999999.
                  Les autres cast INTEGER, FLOAT, LONG retourne une erreur.

                  Merci pour toutes ces explications et ces idées.

                  • [^] # Re: Le bug c'est la requete

                    Posté par . Évalué à 0.

                    En DECIMAL , J'ai un nombre identique quelques soit la date 999999999.

                    Oui c'est vrai, j'oublie toujours à quel point c'est ch… de transformer les timestamp en decimal sous MySQL.
                    En fait tu peux faire un select microseconds(expr) pour récupérer la partie décimale.

                    Mais bon vu que tu as déjà déterminer que le problème venait d'une comparaison de strings…

      • [^] # Re: Le bug c'est la requete

        Posté par . Évalué à 3.

        pour l'URL: http://forge.mysql.com/wiki/Optimizer_Resources
        et plus precisement:
        http://forge.mysql.com/wiki/How_does_the_MySQL_Optimizer_work#Cost-based_query_optimization

        ca ne sera pas forcement effectue plus rapidement (en fonction du dataset) mais ca repond a une logique qui va au-dela du nombre de comparaisons.

      • [^] # Re: Le bug c'est la requete

        Posté par . Évalué à 0. Dernière modification le 21/04/12 à 13:26.

        expliquant comment le système est capable d'effectuer & d'optimiser plus rapidement deux comparaisons plutôt qu'une seule !

        abs(A-B) < C
        
        

        Ca va quasiment aussi vite que

        A <= B
        
        

        Surtout sur des formats ou le signe est porté par un bit de poids fort. dasn ce cas le abs() c'est juste un xor.

    • [^] # Re: Le bug c'est la requete

      Posté par . Évalué à 5.

      Qu'un supérieur ou égale ait le comportement d'un supérieur stricte lorsque l'on passe d'un full scan à l'utilisation d'un index me semble bien correspondre à la définition d'un bug. Le fait que le problème se situe entre la chaise est le clavier est une probabilité de cause, pas une vérité absolue.

Suivre le flux des commentaires

Note : les commentaires appartiennent à ceux qui les ont postés. Nous n'en sommes pas responsables.