web-dev-qa-db-fra.com

Pourquoi les transactions de base de données en lecture seule Spring / Hibernate s'exécutent-elles plus lentement qu'en lecture-écriture?

J'ai fait des recherches sur les performances des transactions de base de données en lecture seule et en lecture-écriture. Le serveur MySQL est distant via une liaison VPN lente, il est donc facile pour moi de voir les différences entre les types de transaction. C'est avec le pool de connexions que je sais fonctionne sur la base de la comparaison des 1er et 2e appels JDBC.

Lorsque je configure le Spring AOP pour utiliser une transaction en lecture seule sur mon appel DAO, les appels sont 30 à 40% plus lents par rapport à la lecture-écriture:

<!-- slower -->
<tx:method name="find*" read-only="true" propagation="REQUIRED" />
...
// slower
@Transaction(readOnly = true)

Contre:

<!-- faster -->
<tx:method name="find*" read-only="false" propagation="REQUIRED" />
...
// faster
@Transaction

En regardant tcpdump, il semble que la transaction en lecture seule fasse plus de va-et-vient avec MySQL. Voici le vidage en lecture seule versus lecture-écriture .

  1. Quelqu'un peut-il expliquer pourquoi les appels en lecture seule prennent plus de temps? Est-ce attendu?

  2. Y a-t-il quelque chose que je fais mal ou quoi que ce soit que je puisse faire pour améliorer leur vitesse en plus d'améliorer le réseau? Je viens de trouver ce post génial avec quelques bonnes recommandations de performances . D'autres commentaires?

Merci beaucoup.

Pourquoi les transactions de base de données en lecture seule Spring/Hibernate s'exécutent-elles plus lentement qu'en lecture-écriture?

Ok, ça a été une course intéressante. Beaucoup pour moi d'apprendre et de partager. Certains des éléments ci-dessous auraient dû être évidents, mais j'espère que mon ignorance et ce que j'ai appris seront utiles aux autres.

<tldr> La réponse courte à la question n ° 1 était que la mise en veille prolongée démarre une session @Transaction(readOnly = true) avec un appel JDBC synchrone set session.transaction.read.only et se termine par un appel set session.transaction.read.write. Ces appels ne sont pas envoyés lors des appels en lecture-écriture, ce qui explique pourquoi les appels en lecture seule étaient plus lents. </tldr>

La réponse plus longue à la question # 2 implique les détails suivants des étapes que j'ai prises pour essayer de réduire les performances de notre base de données distante:

  1. La première chose que nous avons faite a été de changer notre VPN de base de données de TCP vers UDP après avoir lu ceci page d'optimisation OpenVPN . Soupir. J'aurais dû le savoir. J'ai également ajouté le paramètres suivants pour les configurations client et serveur OpenVPN. La surcharge des transactions en lecture seule est passée de 480 ms à 141 ms mais était toujours supérieure aux 100 ms en lecture-écriture. Gros gain.

    ; Got these from: https://community.openvpn.net/openvpn/wiki/Gigabit_Networks_Linux
    proto udp
    tun-mtu 6000
    fragment 0
    mssfix 0
    
  2. En examinant attentivement la sortie tcpdump (tcpdump ... -X Pour la victoire), j'ai remarqué qu'il y avait beaucoup d'appels JDBC de validation automatique et de lecture seule/lecture-écriture inutiles. La mise à niveau vers une version plus récente de la bibliothèque géniale HikariCP connection pool que nous utilisons a aidé à cela. Dans la version 2.4.1, ils ont ajouté des informations qui ont réduit certains de ces appels. Frais généraux de transaction en lecture seule jusqu'à 120 ms. Lecture-écriture toujours à 100 ms. Agréable.

  3. Brett Wooldridge, l'auteur de HikariCP m'a indiqué les paramètres du pilote MySQL qui pourraient aider. Merci beaucoup mec. L'ajout des paramètres suivants à notre URL JDBC MySQL indique au pilote d'utiliser l'état logiciel de la connexion et de ne pas demander l'état au serveur.

    jdbc:mysql://.../database?useLocalSessionState=true&useLocalTransactionState=true
    

    Ces paramètres ont provoqué la suppression d'un plus grand nombre de commandes JDBC synchrones. Les frais généraux de transaction en lecture seule ont chuté à 60 ms et sont désormais les mêmes qu'en lecture-écriture. Woo hoo.

    Édition/AVERTISSEMENT: nous avons en fait annulé l'ajout de useLocalTransactionState=true Après que des bogues ont été trouvés où le pilote n'envoyait pas d'informations sur la transaction.

  4. Mais en regardant davantage la sortie tcpdump, j'ai encore vu des paramètres de transaction en lecture seule/lecture-écriture envoyés. Mon dernier correctif a été d'écrire un pool de détection en lecture seule personnalisé qui distribue des connexions à partir d'un pool spécial s'il voit que le premier appel à la connexion est connection.setReadOnly(true).

    L'utilisation de ce pool personnalisé a réduit la surcharge de transaction pour les connexions en lecture seule et en lecture-écriture à 20 ms. Je pense que cela a essentiellement supprimé le dernier des frais généraux de transaction JDBC. Voici la source du deux classes que j'ai écrites à partir de ma page d'accueil écrire tout cela. Le code est relativement fragile et repose sur Hibernate faisant une connection.setReadOnly(true) première chose mais il semble bien fonctionner et je l'ai documenté dans le XML et le code soigneusement.

Les frais généraux @Transaction Sont donc passés de 480 ms à 20 ms en quelques jours de travail. 100 appels d'hibernation "réels" vers une méthode dao.find(...) ont commencé à 55 secondes et se sont terminés à 4,5 secondes. Joli coup de pied au cul. J'aurais aimé qu'il soit toujours aussi simple d'obtenir une amélioration de la vitesse de 10 fois.

J'espère que mon expérience aide les autres.

30
Gray