đŸ—ƒïž
SGBDR
  • SGBDR
  • Introduction
    • Introduction
    • Abstractions, DDL, DML et SQL
  • Setup initial
    • Options d'architecture
    • MariaDB via Docker (en dev)
    • Connexion
    • Import des donnĂ©es
    • SĂ©curisation et privilĂšges
  • Interrogation
    • Vocabulaire
    • La base "SaaS"
    • select
    • OpĂ©rations de set
    • AgrĂ©gation
    • Sous-requĂȘtes
    • Jointures
    • Pagination
  • Projet
    • Projet 1 : Pagination
  • Data modeling
    • Introduction
    • Design conceptuel
    • Design logique
    • DĂ©pendances fonctionnelles
    • Normalisation
  • Data dĂ©finition (DDL)
    • Introduction
    • Create table
    • Alter table
    • Identifiants
    • Types complexes
    • Exercice
  • Data manipulation (DML)
    • Update et delete
    • Transactions
    • Stored procedures
  • OpĂ©rations
    • Docker en opĂ©ration
    • Optimisation
    • Sauvegardes
  • Conclusion
    • Conclusion
  • Copyright Kevin Glass 2023
Propulsé par GitBook
Sur cette page
  • Identifier et mesurer
  • Outils d'observation
  • Journaux
  • Explain
  • Indexes
  • Index pour les recherches spĂ©ciales
  1. Opérations

Optimisation

Il faut toujours essayer de structurer vos donnĂ©es et vos requĂȘtes pour une performance optimale, en utilisant les techniques dĂ©jĂ  vues dans ce cours :

  • Formes normalisĂ©es, contraintes etc

  • RequĂȘtes :

    • Pas de select * (rĂ©cupĂ©rez uniquement les attributs nĂ©cessaires)

    • Utilisation de la clause where pour bien filtrer les donnĂ©es avant la transmission

    • Utilisation de la pagination

Mais, quand une table devient trĂšs grande, on commence Ă  constater des problĂšmes de performance.

  • On fait une requĂȘte sur un attribut qui n’est pas une clĂ© ?

  • On demande trop de jointures ou de sous-requĂȘtes ?

Comment réagir en temps réel ?

Avant d'optimiser, mesurer !

Identifier et mesurer

Vous constatez une lenteur dans votre application web ou smartphone. Il faut réagir avant de perdre des clients !

La premiĂšre Ă©tape est de lancer une enquĂȘte de la source de la lenteur. En revanche, ce n'est pas si simple parce que la source peut-ĂȘtre Ă  plusieurs endroits de votre stack :

  • Votre logique et/ou code et pas optimale (exemple, dans PHP le templating prend trop de temps ?)

  • La transmission de donnĂ©es prend trop de temps ?

    • Vous transmettez trop de donnĂ©es, ou des donnĂ©es inutiles ?

    • Votre architecture a suffisamment de bande-passante pour la demande ?

  • Le SGBDR rĂ©pond trop lentement ?

    • Les requĂȘtes SQL produisent trop de produits cartĂ©siens ?

    • La SGBDR rĂ©pond vite, mais cette opĂ©ration est trĂšs souvent sollicitĂ©e

    • Les ressources disponibles au SGBDR ne sont pas suffisants ?

Comme vous voyez, il y a plusieurs sources potentielles du problĂšme.

Souvent, la solution la plus rapide, mais la plus chÚre, est-ce qu'on appelle vertical scaling. C'est-à-dire, on paye pour les ressources supplémentaires :

  • payer une meilleure bande passante

  • payer une infrastructure plus puissante pour le SGBDR

En revanche, cette solution n'est pas seulement chĂšre, elle ne traite que les symptĂŽmes du problĂšme.

Pour trouver la cause du problĂšme de performance, il faut maĂźtriser tous les outils Ă  notre disposition afin de trouver la cause exacte :

  • Dans votre API, est-ce qu'il y a des outils d'observation ? console.time et console.timeEnd() dans JavaScript sont des outils simples mais efficaces pour observer oĂč se trouve des bouchons dans votre code.

  • Est-ce que vous avez des journaux que vous pouvez analyser, pour crĂ©er des synthĂšses, et identifier les requĂȘtes les plus lentes ou les plus frĂ©quemment appelĂ©s ?

  • Est-ce que les diffĂ©rentes briques de votre architecture (comme votre SGBDR) disposent des modules d'observation ?

Le processus d'optimisation

  1. Constatez : on commence par constater des ralentissements Ă  certains endroits

  2. Mesurer : on utilise (ou met en place) des outils d'observation afin de noter exactement oĂč se trouve de ralentissements

  3. Identifier : suite aux mesures, on identifie des requĂȘtes, modules, bloques de code, sous-systĂšmes qui prennent le plus de temps

  4. Répéter 2 et 3 : on répÚte de façon drill down, en rentrant dans le détail dans le but de trouver le module le plus bas niveau responsable du problÚme

  5. Tests automatiques : est-ce qu'on a des tests automatiques que valide le comportement des modules concernés par le problÚme ? Sinon, avant l'intervention, rédiger les !

  6. Intervention : on effectue l'opĂ©ration d'optimisation. Une reformulation d'une requĂȘte SQL, la mise en place d'une cache, remanier du code, etc.

  7. Mesurer : est-que notre intervention a amélioré la performance ? Sinon, l'intervention ne sert à rien !

  8. Tester : tourner vos tests automatiques. Votre intervention n'a rien cassé ?

Outils d'observation

MariaDB, comme tous les SGBDR, dispose des outils d'observation qui nous aident dans nos efforts d'observation.

Nous utiliserons une base de données réelle afin de tester nous outils d'observation et optimisation :

Téléchargez et importer cette base dans votre SGBDR local.

Journaux

Il y a deux sortes de journal possible dans MariaDB :

  • slow query log : enregistre des requĂȘtes qui prennent trop de temps Ă  exĂ©cuter

  • general log: enregistre toutes les requĂȘtes

Pour savoir si ces journaux sont activés ou pas :

show variables like 'general_log';

+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| general_log   | OFF   |
+---------------+-------+

Et :

show variables like 'slow_query_log';

+----------------+-------+
| Variable_name  | Value |
+----------------+-------+
| slow_query_log | OFF   |
+----------------+-------+

On voit que par défaut ses journaux sont désactivés (pour optimiser la performance et l'utilisation de la place disque).

# mariadb.cnf

[client-server]
# Port or socket location where to connect
# port = 3306
socket = /run/mysqld/mysqld.sock

# Import all .cnf files from configuration directory
[mariadbd]
skip-host-cache
skip-name-resolve
max_connections=1000
wait_timeout=130

# Activer le journal classic
general_log
log_output=TABLE

# Activer le journal sur les requĂȘtes lentes
slow_query_log
long_query_time=1.0

Nous allons faire quelques requĂȘtes sur notre base de donnĂ©s nutrition :

/* RequĂȘte 1 */
select email, nom, prenom from User where departement like '%haut%'; 

/* RequĂȘte 2 */
select count(type) from Repas where type = 'petit_dejeuner';

Vous constatez des problĂšmes de performance ?

Regardons les journaux !

select * from mysql.general_log order by event_time desc limit 5;
event_time
user_host
thread_id
server_id
command_type
argument

2023-04-18 07:32:27.810283

root[root] @ localhost []

3

1

Query

select * from mysql.general_log order by event_time desc limit 5

2023-04-18 07:31:11.464773

root[root] @ localhost []

3

1

Query

select count(type) from Repas where type = 'petit_dejeuner'

2023-04-18 07:30:06.848528

root[root] @ localhost []

3

1

Query

select email, nom, prenom from User where departement like '%haut%'

Ici, on voit les requĂȘtes triĂ©es par la date d'Ă©vĂ©nement. Mais, on n'a pas de notion de la durĂ©e de chaque requĂȘte.

Regardons l'autre journal :

select * from mysql.slow_log order by start_time desc limit 5;
start_time
user_host
query_time
lock_time
rows_sent
rows_examined
db
last_insert_id
insert_id
server_id
sql_text
thread_id
rows_affected

2023-04-18 07:31:31.304461

root[root] @ localhost []

00:00:19.839693

00:00:00.003751

1

838915

nutrition

0

0

1

select count(type) from Repas where type = 'petit_dejeuner'

3

0

Ici, on constate toutes les requĂȘtes qui ont pris plus qu'une seconde (le seuil Ă©tant dĂ©fini dans la variable long_query_time dans mariadb.cnf).

On a donc mesuré et identifié la source de notre problÚme.

Explain

Le mot clĂ© explain devant une requĂȘte va plutĂŽt dĂ©montrer le plan d'action formulĂ© par le SGBDR au lieu d'exĂ©cuter la requĂȘte. Il peut ĂȘtre utile pour optimiser nos requĂȘtes :

  • Valider que la clĂ© primaire est utilisĂ©, ou bien un attribut indexĂ© est utilisĂ©

  • Valider qu'il n'y a pas trop d'Ă©tapes inutiles dans le plan d'action

  • ...

Vous vous souvenez de notre requĂȘte lente dans la base saas ?

/** Cette requĂȘte prenait plus de 2 minutes Ă  exĂ©cuter */
select distinct S.actionId, S.name, S.budget, S.address_french_regionId
from action as S, action as A
where 
  S.address_french_regionId != 'IDF' and S.address_french_regionId is not null 
  and 
  A.address_french_regionId = 'IDF' 
  and 
  S.budget > A.budget;

Regardons le plan d'action :

explain select distinct S.actionId, S.name, S.budget, S.address_french_regionId
from action as S, action as A
where 
  S.address_french_regionId != 'IDF' and S.address_french_regionId is not null 
  and 
  A.address_french_regionId = 'IDF' 
  and 
  S.budget > A.budget;
id
select_type
table
type
possible_keys
key
key_len
ref
rows
Extra

1

SIMPLE

A

ALL

address_french_regionId

NULL

NULL

NULL

31521

Using where; Using temporary

1

SIMPLE

S

ALL

address_french_regionId

NULL

NULL

NULL

31521

Using where; Using join buffer (flat, BNL join)

Nous avons pu optimiser la requĂȘte en utilisant des sous-requĂȘtes :

explain select actionId, name, budget, address_french_regionId 
 from action
 where
   address_french_regionId != 'IDF' and 
   address_french_regionId is not null and
   budget > some (
     select budget
     from action
     where 
     address_french_regionId = 'IDF'
   );
id
select_type
table
type
possible_keys
key
key_len
ref
rows
Extra

1

PRIMARY

action

ALL

address_french_regionId

NULL

NULL

NULL

31521

Using where

2

SUBQUERY

action

ALL

address_french_regionId

NULL

NULL

NULL

31521

Using where

Il y a deux différences majeures :

  • On voit que dans les deux cas, on n'agit pas sur des clĂ©s primaires ni des attributs indexĂ©s : le champ possible_keys ne contient pas le mot PRIMARY ni de fk....

  • En revanche, on voit dans la colonne extra que la premiĂšre requĂȘte utilise des tampons mĂ©moire pour satisfaire la requĂȘte, ainsi qu'un join buffer (pour le produit cartĂ©sien)

  • On voit que la deuxiĂšme requĂȘte se compose en deux types : PRIMARY puis SUBQUERY qui nous donne un indice que si la sous-requĂȘte s'exĂ©cute en premier, elle va rĂ©duire le champ de possibilitĂ©s pour la requĂȘte PRIMARY

Regardons pour la requĂȘte qu'on vient de faire sur la base nutrition :

explain select count(type) from Repas where type = 'petit_dejeuner';
id
select_type
table
type
possible_keys
key
key_len
ref
rows
Extra

1

SIMPLE

Repas

ALL

NULL

NULL

NULL

NULL

818892

Using where

On voit qu'il n'y a aucune clĂ© primaire, Ă©trangĂšre ni indexe utilisĂ© pour la requĂȘte, et qu'on a 818k lignes Ă  traiter ! Il y a de la place pour optimiser !

Dans cette requĂȘte, on filtre par l'attribut type, qui en soit, n'est pas une clĂ© primaire. Si on peut organiser nos donnĂ©es pour plus facilement extraire des repas d'un certain type, on pourrait Ă©ventuellement optimiser notre requĂȘte.

Pour cela, on utilise des index.

Indexes

Ajouter un index est une façon d’optimiser nos requĂȘtes. On sait dĂ©jĂ  ce qu'une recherche par dichotomie est plus optimisĂ©e :

Alors pourquoi pas en profiter dans notre SGBDR !

En créant un index, on dit au SGBDR d'en effet créer une arborescence de données comme dans l'image pour plus rapidement trouver des données.

Créons un index pour l'attribut type :

create index ind_repas_type on Repas (type);

Vous allez remarquer que la création prend un peu de temps : c'est la construction de l'arborescence.

À tout moment, on peut afficher tous les index dĂ©jĂ  en place :

show index from Repas;

Vous allez noter le mot BTREE dans les résultats. Cela veut dire binary tree qui est le nom anglais de la structure utilisé pour la recherche par dichotomie.

Ou bien supprimer un index :

drop index ind_repas_type on Repas;

Clés primaires, valeurs uniques, contraintes par clé étrangÚre

On sait comment toutes ses fonctionnalités sont implémentées dans notre SGBDR ? Grùce aux index ! Quand on crée une clé primaire, ou désigne un attribut comme unique, le SGBDR crée automatiquement un index pour l'attribut en question.

Par la suite, il serait plus facile et rapide pour le SGBDR de savoir si on crée des doublons !

Analysons le plan d'action pour notre requĂȘte lente maintenant :

explain select count(type) from Repas where type = 'petit_dejeuner';
id
select_type
table
type
possible_keys
key
key_len
ref
rows
Extra

1

SIMPLE

Repas

ref

ind_repas_type

ind_repas_type

1023

const

409446

Using where; Using index

On voit maintenant qu'on utilise bien l'indexe !

Testons ! Est-ce qu'on a rĂ©ussi Ă  optimiser la requĂȘte ?

select count(type) from Repas where type = 'petit_dejeuner';
1 row in set (0.064 sec)

Index pour les recherches spéciales

On utilise aussi des index quand une requĂȘte agit sur un type de donnĂ©e non-standard. À la place de la dichotomie, les donnĂ©es seront organisĂ©es d'une façon d'optimiser la requĂȘte dans le domaine particulier de l'attribut.

Par exemple, pour les coordonnées géométriques, par exemple GPS, on va utiliser une représentation 2D de nos points de données, et ensuite, on va utiliser une structure comme un BSPTree (binary space partition tree) pour plus rapidement chercher des points proches ou dans un rayon précis.

Dans MariaDB, il y a des index non-standards :

FULLTEXT

  • Effectuer des requĂȘtes de type match qui fonctionnent plus comme un moteur de recherche.

SPATIAL

  • Effectuer des requĂȘtes gĂ©o-spatial avec les coordonnĂ©es GPS (par exemple).

PrécédentDocker en opérationSuivantSauvegardes

DerniĂšre mise Ă  jour il y a 1 an

On peut les activer en modifiant :

Exercice Créez le fichier mariadb.cnf et l'inclure dans votre Container local . Validez avec les commandes dessus que les journaux soient bien activés.

À noter, en pratique, il serait prĂ©fĂ©rable d’enregistrer les journaux vers un fichier au lieu d’une table (log_output=FILE) avec l’option logrotate. DĂ©tails ici :

Pour plus d'information sur l'outil explain, je vous dirige vers la page de documentation :

La base nutrition
le fichier de configuration de MariaDB
en suivant les instructions sur cette page
https://mariadb.com/kb/en/rotating-logs-on-unix-and-linux/
https://dev.mysql.com/doc/refman/8.0/en/explain-output.html
https://mariadb.com/kb/en/full-text-index-overview/
https://mariadb.com/kb/en/spatial-index/