Maison > Article > développement back-end > Explication détaillée des tests de performances de Python ORM basé sur le benchmark TPC-C
Lorsque l'application doit être utilisée avec la base de données, le mappeur de relation objet (ORM) est généralement utilisé dans la programmation Python. Des exemples d'ORM Python sont SQLAlchemy, Peewee, Pony-ORM et Django. Le choix des performances ORM joue un rôle essentiel. Mais comment ces ensembles d'outils se comparent-ils ? Les références de performances ORM fournissent une mesure claire, mais il reste encore beaucoup à faire. J'ai recherché et étendu des benchmarks ORM qualitatifs pour aider les développeurs dans le besoin. Le benchmark qualitatif Python ORM Tortoise ORM (lien vers le référentiel) analyse la vitesse de six ORM pour 11 requêtes SQL.
Recommandations d'apprentissage associées :Habituellement, le benchmark Tortoise peut évaluer la vitesse d'exécution des requêtes de divers ORM. Il existe cependant un défaut dans cette méthode de test : la plupart des ORM sont choisis pour les applications web. Dans ce cas, plusieurs utilisateurs envoient souvent toutes sortes de requêtes à la base de données. Parce qu'il n'existe aucun outil d'analyse comparative évalué capable d'évaluer les performances de Python ORM dans ce contexte, j'ai décidé d'écrire mes propres PonyORM et SQLAlchemy pour comparer. Comme base, j'ai utilisé le benchmark TPC-C. Depuis 1988, TPC développe et teste dans le domaine du traitement des données. Ils sont depuis longtemps devenus un standard de l'industrie, presque tous les fournisseurs d'appareils les utilisant sur divers échantillons de matériel et de logiciels. La principale caractéristique de ces tests est qu’ils se concentrent sur des tests sous des charges énormes aussi proches que possible des conditions réelles. TPC-C simule un réseau d'entrepôt. Il consiste en une combinaison de cinq transactions de différents types et complexités exécutées simultanément. Le but de ce test est d'évaluer la vitesse de traitement des transactions lorsque plusieurs utilisateurs virtuels accèdent simultanément à la base de données. J'ai décidé de tester deux ORM Python (SQLALchemy et PonyORM) en utilisant la méthode de test TPC-C adaptée à cette tâche. Le but de ce test est d'évaluer la vitesse de traitement des transactions lorsque plusieurs utilisateurs virtuels accèdent simultanément à la base de données. Instructions de test La première étape consiste à créer et à alimenter la base de données du réseau d'entrepôts. La base de données contient huit relations : 1. Entrepôt 2. District 3. Commande 4. Ligne de commande 5. Stocks 6. Projets 7. Clients 8. Historique Les bases de données de Pony et SQLAlchemy sont les mêmes . Seules les clés primaires et étrangères sont indexées. Little A créera automatiquement ces index. Dans SQLAlchemy, je l'ai créé manuellement. Lors des tests, plusieurs utilisateurs virtuels ont envoyé différents types de transactions à la base de données. Chaque transaction contient plusieurs requêtes. Il existe au total cinq types de transactions soumises pour traitement avec différentes probabilités d'occurrence : Transactions : 1. Nouvelles commandes-45% 2. Paiement-43 % 3. order_status-4% 4. Livraison-4% 5. Niveau de stock-4% Probabilité que la transaction se produise par rapport à l'original TPC- Identique au test C. Cependant, gardez à l'esprit qu'en raison de limitations techniques et du fait que je souhaitais tester les performances des processeurs suivants, l'original a été réalisé sur un serveur avec plus de 64 Go de RAM (nécessitant beaucoup de processeurs et espace disque énorme) test TPC-C. L'ORM plutôt que la capacité du matériel à supporter d'énormes charges, ce test est donc quelque peu simplifié. Les principales différences par rapport au test TPC-C sont les suivantes : Les principales différences : 1. Ce test exécute moins d'utilisateurs virtuels que le test original
2 . Mon test comporte moins d'entrées de tableau. Par exemple : Le nombre d'entrées dans la relation « Inventaire » dans le test d'origine a été calculé à l'aide de la formule 100 000 * W, où W est le nombre d'entrepôts. Dans ce test, c'est 100*W. 3. Dans TPC-C, certaines transactions disposent de plusieurs options pour interroger les données de la base de données. Par exemple, dans une opération de paiement, il existe une possibilité de rechercher le client dans la base de données par identifiant, et une autre par nom et prénom. Actuellement, mon test n'effectue des appels que par pièce d'identité. 4. Ma base de données de test a une table de moins que TPC-C. Dans les tests TPC-C, une fois qu'une commande est créée, elle est ajoutée aux tables Order et NewOrder. Une fois la commande livrée, elle est supprimée de la table NewOrder. Cela peut accélérer les choses lors de l'application d'un grand nombre de transactions par minute ; mais comme j'ai moins d'utilisateurs accédant à la base de données, cela n'est pas nécessaire. Au lieu de cela, dans la table Order, j'ai ajouté l'attribut booléen "is_o_delivered" qui sera False jusqu'à ce que la commande soit livrée. Ensuite, je décrirai brièvement le rôle de chaque transaction. Nombre de transactions Nouvelle commande 1. Passer deux paramètres à la transaction : ID d'entrepôt et ID client 2. Utiliser l'ID transmis de Sélectionnez l'entrepôt et le client dans la base de données 3. Sélectionnez au hasard une zone d'entrepôt dans la base de données 4. Générez un nombre aléatoire indiquant le nombre de lignes de commande. 5. Créer un objet Commande
6. Boucle pour créer des objets OrderLine. A chaque itération de la boucle, sélectionnez un article aléatoire dans la table des articles
7. Changez l'inventaire de chaque article dans la commande
Paiement
1. Changez les deux Paramètres transmis à la transaction : ID d'entrepôt et ID client
2. Sélectionnez l'entrepôt et le client dans la base de données par l'ID transmis
3. Sélectionnez au hasard une zone d'entrepôt dans la base de données
4. Générer un nombre aléatoire indiquant le montant du paiement
5. Augmenter le solde de l'entrepôt et de la région du montant du paiement
6. Diminuer le solde du client du montant du paiement
7. Augmenter le compteur de paiement client
8. Augmenter la somme du montant du paiement client
9. Créer un objet historique
Statut de la commande
1. Transmettez l'identifiant client comme paramètres de transaction de
2. Sélectionnez le client par identifiant et la dernière commande du client
3. Obtenez le statut de la commande et la ligne de commande de la commande.
Livraison
1. Passez l'ID de l'entrepôt comme paramètre de transaction
2. Sélectionnez l'entrepôt et toutes ses zones dans la base de données
3. Pour chaque La région sélectionne la commande non livrée la plus ancienne.
4. Pour chaque commande qui change le statut de livraison en Vrai
5. Pour chaque client avec une quantité de commande croissante
Niveau d'inventaire
1. Passez le ID d'entrepôt comme paramètre de transaction
2. Sélectionnez l'entrepôt dans la base de données par ID
3. Sélectionnez les 20 dernières commandes de l'entrepôt
4. Pour la commande Pour chaque projet, évaluez le niveau d'inventaire du projet
Résultats des tests
Il y a deux ORM participant au test :
1. SQLAlchemy (ligne bleue sur le graphique)
2. PonyORM (ligne orange sur le graphique)
Voici les résultats de l'exécution du test pendant 10 minutes en accédant à la base de données via 2 processus parallèles. Démarrez le processus à l'aide du module Multiprocessing.
Tout d'abord, comme prévu dans le test TPC-C, j'ai testé les cinq transactions. Le résultat de ce test est que la vitesse de Little A est environ deux fois plus rapide qu’auparavant.
Vitesse moyenne :
· Petites transactions A-2543/minute
· SQLAlchemy-1353.4 transactions/minute
Après cela, j'ai décidé d'évaluer cinq transactions séparément Performances ORM transactionnelles. Vous trouverez ci-dessous les résultats pour chaque transaction.
Nouvelle commande
Vitesse moyenne :
· Petites transactions A-3349.2/min
· SQLAlchemy-1415.3 transactions/min
Paiement
Vitesse moyenne :
Petites transactions A-7175.3/minute
SQLAlchemy-4110.6 transactions/minute
Statut de la commande
Moyenne vitesse :
Petit A-16645.6 transactions/minute
SQLAlchemy-4820.8 transactions/minute
Livraison
Vitesse moyenne :
· SQLAlchemy-716.9 transactions/minute
· Petites transactions A-323.5/minute
Niveau d'inventaire
Vitesse moyenne :
· Petites transactions A-677.3/ minute
SQLAlchemy-167.9 transactions/minute
Analyse des résultats des tests
Après avoir reçu les résultats, j'ai analysé pourquoi cela s'est produit et suis arrivé à la conclusion suivante :
Dans 4 transactions sur 5, PonyORM est plus rapide car lors de la génération du code SQL, PonyORM mémorise les résultats de la conversion des expressions Python en SQL. Par conséquent, Pony ne convertit pas à nouveau l'expression lorsque la requête est répétée, tandis que SQLAlchemy est obligé de générer du code SQL à chaque fois qu'il doit exécuter la requête.
Exemple d'une telle requête dans Pony :
stocks = select(stock for stock in Stock if stock.warehouse == whouse and stock.item in items).order_by(Stock.id).for_update()
SQL généré :
SELECT “stock”.”id”, “stock”.”warehouse”, “stock”.”item”, “stock”.”quantity”, “stock”.”ytd”, “stock”.”order_cnt”, “stock”.”remote_cnt”, “stock”.”data”FROM “stock” “stock”WHERE “stock”.”warehouse” = %(p1)s AND “stock”.”item” IN (%(p2)s, %(p3)s)ORDER BY “stock”.”id”FOR UPDATE {‘p1’:7, ‘p2’:7, ‘p3’:37} SQLAlchemy: stocks = session.query(Stock).filter( Stock.warehouse == whouse, Stock.item.in_( items)).order_by(text(“id”)).with_for_update()
SQL généré :
SELECT stock.id AS stock_id, stock.warehouse_id AS stock_warehouse_id, stock.item_id AS stock_item_id, stock.quantity AS stock_quantity, stock.ytd AS stock_ytd, stock.order_cnt AS stock_order_cnt, stock.remote_cnt AS stock_remote_cnt, stock.data AS stock_dataFROM stockWHERE stock.warehouse_id = %(warehouse_id_1)s AND stock.item_id IN (%(item_id_1)s, %(item_id_2)s) ORDER BY id FOR UPDATE {‘warehouse_id_1’: 7, ‘item_id_1’: 53, ‘item_id_2’: 54}
Cependant, évidemment, SQLAlchemy peut être plus rapide exécution de transactions de type livraison car il peut combiner plusieurs opérations UPDATE appliquées à différents objets en une seule commande.
Exemple :
INFO:www.zpedu.com/sqlalchemy.engine.base.Engine:UPDATE order_line SET delivery_d=% (delivery_d)s WHERE order_line.id = %(order_line_id)s INFO:sqlalchemy.engine.base.Engine:( {‘delivery_d’: datetime.datetime(2020, 4, 6, 14, 33, 6, 922281), ‘order_line_id’: 316}, {‘delivery_d’: datetime.datetime(2020, 4, 6, 14, 33, 6, 922272), ‘order_line_id’: 317}, {‘delivery_d’: datetime.datetime(2020, 4, 6, 14, 33, 6, 922261))
Dans ce cas, Little A enverra une requête distincte pour chaque mise à jour.
Conclusion
Sur la base des résultats des tests, je peux dire que Pony sélectionne plus rapidement dans la base de données. En revanche, dans certains cas, SQLAlchemy peut générer des requêtes de type Update à une vitesse plus élevée.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!