Gestion des entity et performance

Bonjour,

Notre Dolibarr ne gère qu’une seule entité.
Je remarque que dans toutes les requêtes sql (ou presque) on recherche les données pour l’entité sélectionnée. (… IN entity IN (1))

Pour une requête du type
SELECT MAX(te.id) FROM llx_actioncomm as te WHERE te.id < ‹ 1715620 › AND te.entity IN (1)
Le temps de réponse est de plus d’une seconde, alors que la requête
SELECT MAX(te.id) FROM llx_actioncomm as te WHERE te.id < ‹ 1715620 ›
est immédiate.

Est-il possible d’avoir un flag global qui permettrait de na pas ajouter « AND entity IN (1) » pour une installation mono entity ?

merci,
Jérôme

Bonjour,

Dolibarr a pris la décision d’aller vers la possibilité du multi-société, donc toutes les requêtes sont adaptées ou vont l’être. Cette évolution est souhaitable pour beaucoup d’utilisateurs.
Après, on est dans du logiciel libre, donc si vous présentez une évolution de code, documenté sur GitHub ça peut passer !

Par contre, qu’il y ai une très grosse différence de performance entre ces deux requêtes n’est pas normal => Vous devez avoir un problème sur votre installation !

image

Effectivement la requête avec te.id < ‹ 1 › répond en 1 ms.
Mais avec la valeur 1720110 ca met 1s05

J’ai vérifié il y a bien les index sur les deux colonnes

Bonjour,

Vous pouvez effectivement avoir une grosse différence de performance dans ce cas.
La requête sans le IN (1) ne doit même pas nécessiter d’interroger la table, le résultat devrait se trouver directement dans l’index. L’autre requête va nécessiter l’utilisation d’un WHERE sur la table ce qui va dégrader les performances.

Si vous lancez vos requêtes en les préfixant avec EXPLAIN vous aurez plus de détails sur la manière dont les requêtes sont executées.

L’explain me donne ceci :

mysql> explain SELECT MAX(te.id) FROM llx_actioncomm as te WHERE te.id < '1720110' AND te.entity IN (1);
+----+-------------+-------+------------+-------+---------------------------+---------+---------+------+--------+----------+-------------+
| id | select_type | table | partitions | type  | possible_keys             | key     | key_len | ref  | rows   | filtered | Extra       |
+----+-------------+-------+------------+-------+---------------------------+---------+---------+------+--------+----------+-------------+
|  1 | SIMPLE      | te    | NULL       | range | PRIMARY,uk_actioncomm_ref | PRIMARY | 4       | NULL | 624310 |    10.00 | Using where |
+----+-------------+-------+------------+-------+---------------------------+---------+---------+------+--------+----------+-------------+

Donc mon idée de mettre une variable globale pour ne pas ajouter where entity in(1) dans le cas d’un mono site a du sens ?
Si oui, on peut effectuer les modifications

Bonjour,
en fait il me semblerait assez « futé » de faire un remplacement de

$sql .= ' AND entity in ('.getEntity('accounting_account', 0).')';

par $sql .= andEntity();

et la fonction andEntity() ferait le test si multientité ou pas et retournerait donc du code sql ou pas

mais c’est bien plus délicat pour les endroits où on a des « AND / OR » qui vont s’enquiller dans la requête, on souffre alors d’absence d’une couche d’ORM avancée …

exemple

$sql .= ' WHERE p.entity IN ('.getEntity('product').')';
if (strlen(trim($search_current_account))) {
	$sql .= natural_search((empty($conf->global->MAIN_PRODUCT_PERENTITY_SHARED) ? "p." : "ppe.") . $accountancy_field_name, $search_current_account);
}
if ($search_current_account_valid == 'withoutvalidaccount') {
	$sql .= " AND aa.account_number IS NULL";
}
if ($search_current_account_valid == 'withvalidaccount') {
	$sql .= " AND aa.account_number IS NOT NULL";
}

dans ce cas on pourrait avoir un « where » qui serait « vide », nous pourrions alors adopter une « astuce » qui consiste à systématiquement avoir « WHERE 1=1 » puis sql .= andEntity() …

bref c’est un sujet qui pourrait tout a fait être abordé mais d’une manière générale comme le dit @ksar la demande globale est plutôt d’être « multicompany » natif …

Ce n’est pas si simple. Vous avez une différence de performance sur cette requête car elle est très simple, et que dans le cas le plus simple elle va pouvoir renvoyer un résultat directement depuis l’index de la table. Ce cas n’est réellement possible que pour des requêtes très simples, surtout avec InnoDB.

Cela ne signifie absolument pas que vous allez avoir une différence de performance sur les requêtes plus complexes qui permettent à Dolibarr de fonctionner.

sujet intéressant… ping @regis @eldy

et en faisant l’inverse ? :

SELECT MAX(te.id) FROM llx_actioncomm as te WHERE te.entity IN (1) AND te.id < 1715620

J’obtiens le même temps de traitement

Ce qui est finalement assez logique, le moteur interne d’optimisation doit faire ce genre de choses tout seul :slight_smile:

L’idée de supprimer le filtre sur entité peut poser de grave problème. En effet, le module peut avoir été activé temporairement puis désactivé. Il faut rester limiter à son entité.
Par contre l’exemple que tu as donné est assez étrange. Tu as mis:

SELECT MAX(te.id) FROM llx_actioncomm as te WHERE te.id < ‹ 1720110 › AND te.entity IN (1);

Hors tu devrais avoir

SELECT MAX(te.id) FROM llx_actioncomm as te WHERE te.id < 1720110 AND te.entity IN (1);

Les simples quotes qui transforme le format en chaine peut avoir un effet sur le plan d’execution.

En quelle version de dolibarr est tu ?
Peux tu refaire le explain plan pour ces 2 requetes APRES avoir mis à jour les stats de la base par
ANALYZE TABLE llx_actioncomm;

explain SELECT MAX(te.id) FROM llx_actioncomm as te WHERE te.id < 1720110 AND te.entity IN (1);
explain SELECT MAX(te.id) FROM llx_actioncomm as te WHERE te.id < 1720110 AND te.entity = 1;

Ayant une idée de la réponse,j’anticipe en donnant une astuce qui peut fonctionner et régler le pb sans aucun dev: Ajoute simplement cet index:
CREATE INDEX idx_actioncomm_entity_id ON llx_actioncomm(entity, id);

Puis refaire à nouveau le explain plan et la mesure de perf. Tu devrais avoir les memes perf que si tu n’avais pas mis le filtre sur entity.

Dans l’attente de tes retours.

Les deux requêtes me donnent le même explain

+----+-------------+-------+------------+-------+---------------------------+---------+---------+------+--------+----------+-------------+
| id | select_type | table | partitions | type  | possible_keys             | key     | key_len | ref  | rows   | filtered | Extra       |
+----+-------------+-------+------------+-------+---------------------------+---------+---------+------+--------+----------+-------------+
|  1 | SIMPLE      | te    | NULL       | range | PRIMARY,uk_actioncomm_ref | PRIMARY | 4       | NULL | 562328 |    10.00 | Using where |
+----+-------------+-------+------------+-------+---------------------------+---------+---------+------+--------+----------+-------------+

Le traitement prend le même temps, toujours un peu plus d’une seconde

La commande ANALYZE me répond OK

Avec ou sans quote pour la valeur de l’id ne change rien au temps de traitement.

Un index sur entity est suffisant.

CREATE INDEX idx_actioncomm_entity ON llx_actioncomm(entity);

Résultat renvoyé par EXPLAIN optimal du coup

id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE Select tables optimized away
1 « J'aime »

Effectivement ça règle mon problème.

Je pensais bêtement que la contrainte d’unicité sur ref et entity ne nécessitait pas de faire un index sur entity.

Merci beaucoup

@eldy est-ce qu’il faut ajouter un index sur le champ entity sur toutes les tables ?

Je ne pense pas. Pas pour l’instant. Il y a encore une anomalie dans l’échange que je ne comprend pas.
L’index devrait être utile si il est combiné mais pas seul. Et je n’arrive pas à reproduire la lenteur, mais sans index.