Les requêtes UPDATE de vos deux questions précédentes ( Question1 , Question2 ) atteignent la table 'people' de PRIMARY KEY avec verrouillage au niveau de la ligne. C'est ce que j'ai déclaré dans la Question 1 du 6 juin 2011 à 10 h 03.
Toutes les transactions traversent la clé PRIMARY. Étant donné que PRIMARY est un index cluster dans InnoDB, la clé PRIMARY et la ligne elle-même sont ensemble. Ainsi, parcourir une ligne et et la CLÉ PRIMAIRE sont une seule et même chose. Par conséquent, tout verrou d'index sur la CLÉ PRIMAIRE est également un verrou de niveau ligne.
Quelque chose d'autre n'a pas encore été envisagé qui peut attribuer la lenteur aux index: L'utilisation d'index NON-UNIQUE dans InnoDB. Chaque recherche indexée dans InnoDB utilisant des index non uniques a également le rowID de chaque ligne attaché à la clé non unique.Le rowID émane essentiellement de l'index clusterisé . La mise à jour des index non uniques DOIT TOUJOURS interagir avec l'index cluster, MÊME SI LA TABLE N'A PAS DE CLÉ PRIMAIRE.
Une autre chose à penser est le processus de gestion des nœuds BTREE dans un index. Parfois, cela nécessite le fractionnement de page des nœuds. Toutes les entrées du nœud BTREE d'index non uniques contiennent des champs non uniques PLUS le rowID dans l'index cluster. Pour atténuer correctement le fractionnement de ces pages BTREE sans perturber l'intégrité des données, la ligne associée au rowID doit subir un verrouillage de niveau de ligne en interne.
Si la table 'people' a beaucoup d'index non uniques, préparez-vous à avoir un grand nombre de pages d'index dans l'espace de table ainsi que d'avoir de minuscules petits verrous de lignes se faufiler de temps en temps.
Il y a un autre facteur qui n'est pas aussi évident: la population clé
Parfois, lorsqu'un index est rempli, les valeurs de clé constituant les index peuvent devenir déséquilibrées au fil du temps et faire en sorte que MySQL Query Optimizer passe des recherches par clé, aux analyses d'index et enfin aux analyses de table complètes. Que vous ne pouvez pas contrôler à moins que vous ne repensiez la table avec de nouveaux index pour compenser les touches de déséquilibre. Veuillez fournir la structure de la table pour la table «personnes», le nombre de la table «personnes» et la sortie des index pour la table «personnes» .
Même si les requêtes n'utilisent que la PRIMARY KEY, le déséquilibre des clés dans les index non uniques nécessite toujours un équilibrage BTREE et un fractionnement de page. Une telle gestion de BTREE produira un ralentissement notable en raison de verrouillages de niveau de ligne intermittents que vous n'aviez pas l'intention de produire.
MISE À JOUR 2011-06-14 22:19
Requêtes de la question 1
UPDATE people SET company_id = 1610, name = '<name>', password = '<hash>',
temp_password = NULL, reset_password_hash = NULL, email = '<redacted>@yahoo.com',
phone = NULL, mobile = '<phone>', iphone_device_id = 'android:<id>-<id>',
iphone_device_time = '2011-06-06 05:35:09', last_checkin = '2011-06-06 05:24:42',
location_lat = <lat>, location_long = -<lng>, gps_strength = 3296,
picture_blob_id = 1190,
authority = 1, active = 1, date_created = '2011-04-13 20:21:20',
last_login = '2011-06-06 05:35:09', panic_mode = 0,
battery_level = NULL, battery_state = NULL WHERE people_id = 3125
UPDATE people SET company_id = 1610, name = '<name>', password = '<hash>',
temp_password = NULL, reset_password_hash = NULL, email = '<redacted>@yahoo.com',
phone = NULL, mobile = '<phone>', iphone_device_id = 'android:<id>-<id>-<id>-<id>',
iphone_device_time = '2011-06-06 05:24:42', last_checkin = '2011-06-06 05:35:07',
location_lat = <lat>, location_long = -<lng>, gps_strength = 3296,
picture_blob_id = 1190,
authority = 1, active = 1, date_created = '2011-04-13 20:21:20',
last_login = '2011-06-06 05:35:09', panic_mode = 0,
battery_level = NULL, battery_state = NULL WHERE people_id = 3125
Imaginez la séquence des événements
- Trouvez la rangée par PRIMARY KEY
- Verrouiller la ligne et l'index clusterisé
- Créer des données MVCC pour toutes les colonnes en cours de mise à jour
- Quatre colonnes sont indexées (email, company_id, iphone_device_id, picture_blob_id)
- Chaque indice nécessite la gestion de BTREE
- Dans le même espace de transaction, les étapes 1 à 5 tentent de se répéter sur la même ligne, en mettant à jour les mêmes colonnes (envoyer le même dans les deux requêtes, company_id le même dans les deux requêtes, picture_blob_id le même dans les deux requêtes, iphone_device_id différent)
Requêtes de la question 2
UPDATE people SET iphone_device_id=NULL
WHERE iphone_device_id='iphone:<device_id_blah>' AND people_id<>666;
UPDATE people SET company_id = 444, name = 'Dad', password = '<pass>',
temp_password = NULL, reset_password_hash = NULL, email = '<redacted>@gmail.com',
phone = NULL, mobile = NULL, iphone_device_id = 'iphone:<device_id_blah>',
iphone_device_time = '2011-06-06 19:12:29', last_checkin = '2011-06-07 02:49:47',
location_lat = <lat>, location_long = <lng>, gps_strength = 66,
picture_blob_id = 1661,
authority = 1, active = 1, date_created = '2011-03-20 19:18:34',
last_login = '2011-06-07 11:15:01', panic_mode = 0, battery_level = 0.55,
battery_state = 'unplugged' WHERE people_id = 666;
Ces deux requêtes sont encore plus confuses car la première requête met à jour tout sauf people_id 666. Des centaines de lignes sont douloureusement verrouillées avec uniquement la première requête. La deuxième requête met à jour people_id 666 en exécutant la séquence de 5 événements. La première requête exécute ces mêmes 5 séquences d'événements sur chaque ligne impliquée, sauf people_id 666, mais l'index pour iphone_device_id est sur un cours intercepté avec deux requêtes différentes. Quelqu'un doit verrouiller les pages BTREE sur la base du premier arrivé, premier servi.
Face à ces deux paires de requêtes sur une trajectoire de collision pour éventuellement verrouiller les mêmes pages BTREE dans un index peut être une expérience déchirante pour InnoDB ou tout SGBDR conforme à ACID. Ainsi, un ralentissement d'index est le destin de ces paires de requêtes, sauf si vous pouvez garantir que les requêtes s'exécutent avec AUTOCOMMIT = 1 ou en autorisant des lectures sales (bien que des collisions comme celles-ci font de READ-COMMITTED et READ-UNCOMMITED un cauchemar pour MVCC).
MISE À JOUR 2011-06-15 10:29
@RedBlueThing: Dans les requêtes de la question 2, la première requête est une requête de plage, donc de nombreux verrous de ligne sont atteints. Notez également que les deux requêtes tentent de verrouiller le même espace id 0 page n ° 4611 n bits 152 est verrouillé dans la PRIMARY KEY, alias index cluster.
Afin de vous assurer que votre application fonctionne, à tout le moins, en fonction de la série d'événements que vous attendez, vous pouvez essayer deux options différentes:
Option 1) Convertissez ce tableau en MyISAM (au moins sur un serveur de développement). Chaque mise à jour, insertion et suppression imposera un verrou de table complet sur le principe du premier arrivé, premier servi.
Option 2) Essayez d'utiliser le SERIALIZABLE niveau d'isolement . Cela verrouillera toutes les lignes prévues en mode PARTAGÉ.
La séquence d'événements que vous attendez sera interrompue ou réussie en utilisant ces deux options alternatives. Si ces deux options échouent, vous devrez examiner votre application et prioriser l'ordre d'exécution de vos requêtes. Une fois que vous avez établi cette priorité, vous pouvez simplement annuler ces options (pour l'option 1, revenir à InnoDB, pour l'option 2, revenir au niveau d'isolement par défaut [arrêter d'utiliser SERIALIZABLE]).