{"id":79893,"date":"2020-05-01T13:43:12","date_gmt":"2020-05-01T11:43:12","guid":{"rendered":"https:\/\/prohoster.info\/blog\/administrirovanie\/postgresql-i-jdbc-vyzhimaem-vse-soki-vladimir-sitnikov"},"modified":"2020-05-01T13:43:12","modified_gmt":"2020-05-01T11:43:12","slug":"postgresql-i-jdbc-vyzhimaem-vse-soki-vladimir-sitnikov","status":"publish","type":"post","link":"https:\/\/prohoster.info\/fr\/blog\/administrirovanie\/postgresql-i-jdbc-vyzhimaem-vse-soki-vladimir-sitnikov","title":{"rendered":"PostgreSQL et JDBC, nous tirons le meilleur parti. Vladimir Sitnikov","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p><strong>Je vous propose de consulter la transcription du rapport de d\u00e9but 2016 de Vladimir Sitnikov intitul\u00e9 \"PostgreSQL et JDBC, nous en tirons le maximum\"<\/strong><\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL et JDBC, nous tirons le meilleur parti. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/9d94c8a024bd2821e431c525aae0127d.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL et JDBC, nous tirons le meilleur parti. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/030666abda53aa388b1cb1d0c46a7524.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Bonjour ! Je m'appelle Vladimir Sitnikov. Je travaille depuis 10 ans chez NetCracker et je me consacre principalement \u00e0 la performance. Tout ce qui touche \u00e0 Java et tout ce qui concerne SQL, c'est ce que j'aime. <\/p>\n<p><\/p>\n<p>Aujourd'hui, je vais vous parler des d\u00e9fis que nous avons rencontr\u00e9s dans notre entreprise lorsque nous avons commenc\u00e9 \u00e0 utiliser PostgreSQL comme serveur de bases de donn\u00e9es. Nous travaillons principalement avec Java. Cependant, ce dont je vais parler aujourd'hui concerne \u00e9galement d'autres langages. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL et JDBC, nous tirons le meilleur parti. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/b01a214d782c7e32797a6c6466457979.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Nous allons parler de :<\/p>\n<p><\/p>\n<ul>\n<li>la s\u00e9lection de donn\u00e9es. <\/li>\n<li>de la sauvegarde des donn\u00e9es. <\/li>\n<li>et \u00e9galement de la performance. <\/li>\n<li>Et des pi\u00e8ges dissimul\u00e9s qui peuvent surgir. <\/li>\n<\/ul>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL et JDBC, nous tirons le meilleur parti. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/57da0c6aa4bb62e1beb77160f5aee7e7.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Commen\u00e7ons par une question simple. Nous s\u00e9lectionnons une ligne d'une table par cl\u00e9 primaire. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL et JDBC, nous tirons le meilleur parti. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/d5fcb1172aef402a87064498bf8a5218.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>La base de donn\u00e9es se trouve sur le m\u00eame h\u00f4te. Et tout cela prend 20 millisecondes.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL et JDBC, nous tirons le meilleur parti. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/103b7f9736b82a3313491b2f0d2a5824.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Ces 20 millisecondes, c'est beaucoup. Si vous avez 100 telles requ\u00eates, vous perdez du temps en secondes pour ex\u00e9cuter ces requ\u00eates, c'est-\u00e0-dire que vous gaspillez du temps.<\/p>\n<p><\/p>\n<p>Nous n'aimons pas faire \u00e7a et nous regardons ce que la base nous propose pour cela. La base nous offre deux options d'ex\u00e9cution des requ\u00eates. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL et JDBC, nous tirons le meilleur parti. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/b467e86a0f8c4c32cea182fe27a28e6e.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>La premi\u00e8re option est la requ\u00eate simple. Qu'est-ce qui est bien ? C'est que nous la prenons et l'envoyons, et rien de plus. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL et JDBC, nous tirons le meilleur parti. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/7e333a62ba68feb0781ff96e39486591.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/pgjdbc\/pgjdbc\/pull\/478\">https:\/\/github.com\/pgjdbc\/pgjdbc\/pull\/478<\/a><\/noindex><\/p>\n<p><\/p>\n<p>La base a aussi une requ\u00eate \u00e9tendue qui est plus astucieuse mais plus fonctionnelle. On peut envoyer s\u00e9par\u00e9ment des requ\u00eates pour le parsing, l'ex\u00e9cution, la liaison de variables, etc. <\/p>\n<p><\/p>\n<p>La Super extended query est quelque chose que nous ne couvrirons pas dans cette pr\u00e9sentation. Peut-\u00eatre avons-nous certaines attentes vis-\u00e0-vis de la base de donn\u00e9es, et il existe une liste de souhaits qui est formul\u00e9e d'une certaine mani\u00e8re, c'est-\u00e0-dire ce que nous voulons, mais qui n'est pas r\u00e9alisable actuellement et dans un avenir proche. Nous l'avons donc simplement not\u00e9e et nous irons voir les personnes cl\u00e9s.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL et JDBC, nous tirons le meilleur parti. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/19094729074fd28c5b9f236833a9a266.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Ce que nous pouvons faire, c'est utiliser la requ\u00eate simple et la requ\u00eate \u00e9tendue.<\/p>\n<p><\/p>\n<p>Quelle est la particularit\u00e9 de chaque approche ? <\/p>\n<p><\/p>\n<p>La requ\u00eate simple est bien adapt\u00e9e pour une ex\u00e9cution unique. On l'ex\u00e9cute une fois et on oublie. Le probl\u00e8me, c'est qu'elle ne prend pas en charge le format binaire des donn\u00e9es, donc pour certains syst\u00e8mes \u00e0 haute performance, elle n'est pas appropri\u00e9e.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL et JDBC, nous tirons le meilleur parti. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/062c0e45cefd91ece331a306a7651bde.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>La requ\u00eate \u00e9tendue - permet d'\u00e9conomiser du temps lors de l'analyse. C'est ce que nous avons fait et commenc\u00e9 \u00e0 utiliser. Cela nous a \u00e9t\u00e9 tr\u00e8s, tr\u00e8s utile. Il n'y a pas seulement des \u00e9conomies sur l'analyse. Il y a aussi des \u00e9conomies sur la transmission des donn\u00e9es. Transmettre des donn\u00e9es au format binaire est beaucoup plus efficace. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL et JDBC, nous tirons le meilleur parti. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/6df83a2f3736568668cf9d74de5d9758.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Passons \u00e0 la pratique. Voici \u00e0 quoi ressemble une application typique. Cela peut \u00eatre Java, etc. <\/p>\n<p><\/p>\n<p>Nous avons cr\u00e9\u00e9 une instruction. Nous avons ex\u00e9cut\u00e9 la commande. Nous avons cr\u00e9\u00e9 une fermeture. O\u00f9 est l'erreur ici ? Quel est le probl\u00e8me ? Aucun probl\u00e8me. C'est ce qui est dit dans tous les livres. C'est ainsi que cela doit \u00eatre \u00e9crit. Si vous voulez une performance maximale, \u00e9crivez comme \u00e7a. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL et JDBC, nous tirons le meilleur parti. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/6923293946dec45b3fd508235728ab95.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Mais la pratique a montr\u00e9 que cela ne fonctionne pas. Pourquoi ? Parce que nous avons la m\u00e9thode \u00abclose\u00bb. Et quand nous faisons cela, du point de vue de la base de donn\u00e9es, c'est comme si un fumeur travaillait avec la base de donn\u00e9es. Nous avons dit \u00abPARSE EXECUTE DEALLOCATE\u00bb.<\/p>\n<p><\/p>\n<p>Pourquoi cr\u00e9er et d\u00e9charger ces statements inutiles ? Ils ne sont n\u00e9cessaires \u00e0 personne. Mais g\u00e9n\u00e9ralement, dans PreparedStatement, c'est comme \u00e7a : quand nous les fermons, ils ferment tout dans la base de donn\u00e9es. Ce n'est pas ce que nous voulons. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL et JDBC, nous tirons le meilleur parti. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/5a357a209e414024c437d042f600f251.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Nous voulons, comme des gens sains, travailler avec la base. Une fois pr\u00e9par\u00e9, nous ex\u00e9cutons notre statement plusieurs fois. En fait, plusieurs fois signifie une fois pendant toute la dur\u00e9e de vie de l'application, \u00e0 chaque fois que nous avons analys\u00e9. Et sur diff\u00e9rentes REST, nous utilisons le m\u00eame identifiant de statement. Voici notre objectif. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL et JDBC, nous tirons le meilleur parti. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/ac4aed702a624ad9f9addc4362daf760.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Comment y parvenir ? <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL et JDBC, nous tirons le meilleur parti. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/0f101d890d1a2af8e99f8a3a8a06fd5a.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>C'est tr\u00e8s simple - il ne faut pas fermer les statements. Nous \u00e9crivons comme \u00e7a : \u00abprepare\u00bb \u00abexecute\u00bb. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL et JDBC, nous tirons le meilleur parti. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/0b3fd8fd4f5861d4a76cad5a8421ddad.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL et JDBC, nous tirons le meilleur parti. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/724646bfc55b5e2b611b1584ca8ba6aa.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Si nous lan\u00e7ons quelque chose comme \u00e7a, il est clair qu'\u00e0 un moment donn\u00e9, quelque chose va d\u00e9border. Si ce n'est pas clair, nous pouvons le mesurer. Prenons et \u00e9crivons un benchmark, o\u00f9 cette m\u00e9thode simple sera mise en \u0153uvre. Nous cr\u00e9ons un statement. Nous le lan\u00e7ons sur une certaine version du driver et obtenons qu'il tombe assez rapidement avec une perte totale de la m\u00e9moire que nous avons accumul\u00e9e. <\/p>\n<p><\/p>\n<p>Il est \u00e9vident que de telles erreurs sont facilement corrig\u00e9es. Je ne vais pas en parler. Mais je dirai que dans la nouvelle version, cela fonctionne beaucoup plus rapidement. La m\u00e9thode est inutile, mais n\u00e9anmoins. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL et JDBC, nous tirons le meilleur parti. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/b232a6205e708c70f52be0024bbe9980.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Comment travailler correctement ? Que devons-nous faire pour cela ?<\/p>\n<p><\/p>\n<p>Dans la r\u00e9alit\u00e9, les applications ferment toujours les statements. Tous les livres recommandent de les fermer, sinon la m\u00e9moire fuitera. <\/p>\n<p><\/p>\n<p>Et PostgreSQL ne sait pas mettre en cache les requ\u00eates. Il faut que chaque session cr\u00e9e elle-m\u00eame ce cache. <\/p>\n<p><\/p>\n<p>Et nous ne voulons pas non plus perdre du temps sur l'analyse. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL et JDBC, nous tirons le meilleur parti. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/85b9574938aa6e6bf59c956e3728bdc3.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Et comme d'habitude, nous avons deux options. <\/p>\n<p><\/p>\n<p>La premi\u00e8re option consiste \u00e0 dire que nous allons tout envelopper dans PgSQL. Il y a un cache. Il met tout en cache. Cela devrait bien fonctionner. Nous avons regard\u00e9 cela. Nous avons 100500 requ\u00eates. Cela ne fonctionne pas. Nous ne sommes pas d'accord pour transformer les requ\u00eates en proc\u00e9dures manuellement. Non-non. <\/p>\n<p><\/p>\n<p>Nous avons une deuxi\u00e8me option : prendre et coder nous-m\u00eames. Nous ouvrons le code source et commen\u00e7ons \u00e0 coder. Nous codons-codons. Il s'est av\u00e9r\u00e9 que ce n'est pas si compliqu\u00e9 \u00e0 faire. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL et JDBC, nous tirons le meilleur parti. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/501620b014800406167d20668baef709.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/pgjdbc\/pgjdbc\/pull\/319\">https:\/\/github.com\/pgjdbc\/pgjdbc\/pull\/319<\/a><\/noindex><\/p>\n<p><\/p>\n<p>Cela est apparu en ao\u00fbt 2015. Maintenant, il y a une version plus moderne. Et tout va bien. Cela fonctionne si bien que nous ne changeons rien dans l'application. Et nous avons m\u00eame cess\u00e9 de penser en termes de PgSQL, c'est-\u00e0-dire que cela a suffi \u00e0 r\u00e9duire pratiquement \u00e0 z\u00e9ro toutes les d\u00e9penses. <\/p>\n<p><\/p>\n<p>Les instructions pr\u00e9par\u00e9es du serveur sont activ\u00e9es lors de la cinqui\u00e8me ex\u00e9cution afin de ne pas gaspiller de m\u00e9moire dans la base de donn\u00e9es pour chaque requ\u00eate unique. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL et JDBC, nous tirons le meilleur parti. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/8ee95e71f92187941989ad0c18ece0c0.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>On peut demander : o\u00f9 sont les chiffres ? Que recevez-vous ? Et ici, je ne donnerai pas de chiffres car chaque requ\u00eate a les siens.<\/p>\n<p><\/p>\n<p>Nous avions des requ\u00eates o\u00f9 nous d\u00e9pensions environ 20 millisecondes pour le parsing sur les requ\u00eates OLTP. Il y avait 0,5 millisecondes pour l'ex\u00e9cution, 20 millisecondes pour le parsing. La requ\u00eate \u2013 10 Ko de texte, 170 lignes de plan. C'est une requ\u00eate OLTP. Elle demande 1, 5, 10 lignes, parfois plus. <\/p>\n<p><\/p>\n<p>Mais nous ne voulions absolument pas d\u00e9penser 20 millisecondes. Nous sommes pass\u00e9s \u00e0 0. Tout va bien. <\/p>\n<p><\/p>\n<p>Que pouvez-vous en tirer ? Si vous avez Java, prenez la version moderne du driver et soyez satisfait. <\/p>\n<p><\/p>\n<p>Si vous avez un autre langage, alors pensez : peut-\u00eatre que vous en avez aussi besoin ? Car du point de vue du langage final, par exemple, si c'est PL 8 ou si vous avez LibPQ, il n'est pas \u00e9vident que vous passiez du temps \u00e0 parser, et cela vaut la peine de v\u00e9rifier. Comment ? Tout est gratuit. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL et JDBC, nous tirons le meilleur parti. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/fda6b1c1b126cb85c970e42335e1dc24.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>\u00c0 l'exception des erreurs, certaines particularit\u00e9s. Et nous allons justement en parler maintenant. La plupart portera sur l'arch\u00e9ologie industrielle, sur ce que nous avons trouv\u00e9, sur ce qui nous a interpell\u00e9s. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL et JDBC, nous tirons le meilleur parti. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/9a3b383f83c5e227cd02952c5825b64f.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Si la requ\u00eate est g\u00e9n\u00e9r\u00e9e dynamiquement. Cela arrive. Quelqu'un concat\u00e8ne des cha\u00eenes, ce qui donne une requ\u00eate SQL.<\/p>\n<p><\/p>\n<p>Pourquoi est-elle mauvaise ? Elle est mauvaise car \u00e0 chaque fois nous obtenons en fin de compte une cha\u00eene diff\u00e9rente.<\/p>\n<p><\/p>\n<p>Et cette cha\u00eene vari\u00e9e doit recalculer son hashCode. C'est vraiment une t\u00e2che CPU \u2013 trouver un long texte de requ\u00eate m\u00eame dans le hash existant n'est pas si simple. Donc, la solution est simple \u2013 ne g\u00e9n\u00e9rez pas de requ\u00eates. Conservez-les dans une variable. Et soyez satisfait.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL et JDBC, nous tirons le meilleur parti. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/bcd4f729204cecdb572a98f357bcc33f.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Le probl\u00e8me suivant. Les types de donn\u00e9es sont importants. Il existe des ORM qui affirment que peu importe quel NULL, peu importe lequel. Si c'est un Int, alors nous disons setInt. Et si c'est NULL, alors que ce soit toujours VARCHAR. Et quelle diff\u00e9rence cela fait-il au final quel type de NULL ? La base de donn\u00e9es comprendra tout seule. Et ce tableau ne fonctionne pas. <\/p>\n<p><\/p>\n<p>Dans la pratique, la base de donn\u00e9es ne se soucie pas du tout. <strong>Si vous avez dit une premi\u00e8re fois que c'\u00e9tait un nombre, et la seconde fois que c'\u00e9tait VARCHAR, il est impossible de r\u00e9utiliser les d\u00e9clarations pr\u00e9par\u00e9es du serveur. Dans ce cas, il faut recr\u00e9er notre d\u00e9claration.<\/strong><\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL et JDBC, nous tirons le meilleur parti. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/552d9c2e25cf9abbdfef12c98d027740.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Si vous ex\u00e9cutez la m\u00eame requ\u00eate, faites attention \u00e0 ne pas m\u00e9langer les types de donn\u00e9es dans la colonne. Il faut veiller au NULL. C'est une erreur fr\u00e9quente que nous avons rencontr\u00e9e apr\u00e8s avoir commenc\u00e9 \u00e0 utiliser les PreparedStatements.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL et JDBC, nous tirons le meilleur parti. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/5d73c8f5dfa281c3bda962fc3e5edd84.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>D'accord, nous avons activ\u00e9. Nous avons peut-\u00eatre pris le driver. Et la performance a chut\u00e9. Tout est devenu mauvais. <\/p>\n<p><\/p>\n<p>Comment cela se fait-il ? Un bug ou une fonctionnalit\u00e9 ? Malheureusement, nous n'avons pas pu comprendre \u2013 est-ce un bug ou une fonctionnalit\u00e9. Mais il existe un sc\u00e9nario assez simple pour reproduire ce probl\u00e8me. Il nous a pris par surprise. Et cela concerne une s\u00e9lection litt\u00e9rale \u00e0 partir d'une seule table. Nous avions bien s\u00fbr plus de telles requ\u00eates. En g\u00e9n\u00e9ral, elles impliquaient deux \u00e0 trois tables, mais voici un sc\u00e9nario de reproduction. Prenez n'importe quelle version de votre base et reproduisez.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL et JDBC, nous tirons le meilleur parti. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/fa0fdd037c2eaa31667d5a527bb71f9c.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/gist.github.com\/vlsi\/df08cbef370b2e86a5c1\">https:\/\/gist.github.com\/vlsi\/df08cbef370b2e86a5c1<\/a><\/noindex><\/p>\n<p><\/p>\n<p>L'id\u00e9e est que nous avons deux colonnes, chacune index\u00e9e. Dans une colonne avec la valeur NULL, il y a un million de lignes. Et dans l'autre colonne, il n'y a que 20 lignes. Lorsque nous ex\u00e9cutons sans variables li\u00e9es, tout fonctionne bien. <\/p>\n<p><\/p>\n<p>Si nous commen\u00e7ons \u00e0 ex\u00e9cuter avec des variables li\u00e9es, c'est-\u00e0-dire que nous ex\u00e9cutons le \u00ab ? \u00bb ou \u00ab $1 \u00bb pour notre requ\u00eate, que recevons-nous finalement ?<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL et JDBC, nous tirons le meilleur parti. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/e91c397796c7ddcbade3bc090719e9d1.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/gist.github.com\/vlsi\/df08cbef370b2e86a5c1\">https:\/\/gist.github.com\/vlsi\/df08cbef370b2e86a5c1<\/a><\/noindex><\/p>\n<p><\/p>\n<p>Premi\u00e8re ex\u00e9cution \u2013 comme il se doit. Deuxi\u00e8me \u2013 un peu plus rapide. Quelque chose s'est mis en cache. Troisi\u00e8me-quatri\u00e8me-cinqui\u00e8me. Puis soudain \u2013 et comme \u00e7a. Et le pire, c'est que cela se produit \u00e0 la sixi\u00e8me ex\u00e9cution. Qui savait qu'il fallait faire exactement six ex\u00e9cutions pour comprendre quel \u00e9tait vraiment le plan d'ex\u00e9cution ?<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL et JDBC, nous tirons le meilleur parti. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/893c955bc1e2e15a6986690e516f72a9.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Qui est responsable ? Que s'est-il pass\u00e9 ? La base de donn\u00e9es contient une optimisation. Et elle est, en quelque sorte, optimis\u00e9e pour un cas g\u00e9n\u00e9rique. Et, par cons\u00e9quent, apr\u00e8s un certain temps, elle passe \u00e0 un plan g\u00e9n\u00e9rique, qui, h\u00e9las, peut s'av\u00e9rer \u00eatre diff\u00e9rent. Il peut \u00eatre le m\u00eame, ou il peut \u00eatre diff\u00e9rent. Et il y a une certaine valeur seuil qui conduit \u00e0 ce comportement. <\/p>\n<p><\/p>\n<p>Que peut-on en faire ? Ici, il est bien s\u00fbr plus difficile de faire des hypoth\u00e8ses. Il existe une solution simple que nous utilisons. C'est +0, OFFSET 0. Vous connaissez s\u00fbrement de telles solutions. On prend juste et on ajoute \u00ab +0 \u00bb \u00e0 la requ\u00eate et tout va bien. Je vais le montrer plus tard. <\/p>\n<p><\/p>\n<p>Il y a aussi une autre option : regarder les plans de plus pr\u00e8s. Le d\u00e9veloppeur doit non seulement \u00e9crire la requ\u00eate, mais aussi dire \u00ab explain analyze \u00bb six fois. Si c'est cinq, \u00e7a ne convient pas. <\/p>\n<p><\/p>\n<p>Et il y a aussi une troisi\u00e8me option : \u00e9crire un mail \u00e0 pgsql-hackers. J'ai \u00e9crit, mais pour l'instant, ce n'est pas clair \u2013 est-ce un bug ou une fonctionnalit\u00e9.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL et JDBC, nous tirons le meilleur parti. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/8dc8d3a78e0b794e1ceba20f6ca914ae.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/gist.github.com\/vlsi\/df08cbef370b2e86a5c1\">https:\/\/gist.github.com\/vlsi\/df08cbef370b2e86a5c1<\/a><\/noindex><\/p>\n<p><\/p>\n<p>Pendant que nous r\u00e9fl\u00e9chissons \u2013 est-ce un bug ou une fonctionnalit\u00e9, r\u00e9parons-le. Prenons notre requ\u00eate et ajoutons \u00ab +0 \u00bb. Tout va bien. Deux symboles et m\u00eame pas besoin de penser \u00e0 comment cela fonctionne. C'est tr\u00e8s simple. Nous avons simplement emp\u00each\u00e9 la base de donn\u00e9es d'utiliser l'index sur cette colonne. Nous n'avons pas d'index sur la colonne \u00ab +0 \u00bb et tout va bien, la base de donn\u00e9es n'utilise pas l'index. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL et JDBC, nous tirons le meilleur parti. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/5261d80d084786a15b9764f6367014cd.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Voici la r\u00e8gle des six \u00ab explain \u00bb. Dans les versions actuelles, il faut le faire six fois si vous avez des variables li\u00e9es. Si vous n'avez pas de variables li\u00e9es, alors nous faisons ainsi. Et en fin de compte, c'est justement cette requ\u00eate qui \u00e9choue. Ce n'est pas sorcier.<\/p>\n<p><\/p>\n<p>On pourrait penser, jusqu'\u00e0 quand ? Il y a un bug ici, un bug l\u00e0. Il y a vraiment des bugs partout. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL et JDBC, nous tirons le meilleur parti. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/674dc7e7b4624baa5c79eee9e1e89db0.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Regardons encore. Par exemple, nous avons deux sch\u00e9mas. Sch\u00e9ma A avec la table Y et sch\u00e9ma B avec la table Y. La requ\u00eate \u2013 s\u00e9lectionner des donn\u00e9es de la table. Que se passera-t-il alors ? Nous aurons une erreur. Nous aurons tout ce qui a \u00e9t\u00e9 mentionn\u00e9 pr\u00e9c\u00e9demment. La r\u00e8gle est la suivante : bug partout, nous aurons tout ce qui a \u00e9t\u00e9 mentionn\u00e9 pr\u00e9c\u00e9demment.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL et JDBC, nous tirons le meilleur parti. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/dd6f544bb9e930c39e36523c9afe6003.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Maintenant, la question : \u00ab Pourquoi ? \u00bb. On pourrait penser qu'il y a de la documentation indiquant que, si nous avons un sch\u00e9ma, il y a une variable \u00ab search_path \u00bb, qui indique o\u00f9 chercher la table. On pourrait penser qu'il y a une variable.<\/p>\n<p><\/p>\n<p>Quel est le probl\u00e8me ? Le probl\u00e8me est que les instructions pr\u00e9par\u00e9es par le serveur ne soup\u00e7onnent pas que quelqu'un peut changer le search_path. Cette valeur reste, en quelque sorte, constante pour la base de donn\u00e9es. Et certaines parties peuvent ne pas saisir les nouvelles valeurs. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL et JDBC, nous tirons le meilleur parti. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/f738301606d84d72bfa7cc5b568c0df8.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Bien s\u00fbr, cela d\u00e9pend de la version sur laquelle vous testez. Cela d\u00e9pend de la mesure dans laquelle vos tables diff\u00e8rent. Et la version 9.1 ex\u00e9cutera simplement les anciennes requ\u00eates. Les nouvelles versions peuvent d\u00e9tecter des anomalies et indiquer que vous avez une erreur.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL et JDBC, nous tirons le meilleur parti. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/4761cd92835b94acbf9d6c9d21817316.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/www.postgresql.org\/message-id\/CAB=Je-GQOW7kU9Hn3AqP1vhaZg_wE9Lz6F4jSp-7cm9_M6DyVA@mail.gmail.com\">D\u00e9finir search_path + instructions pr\u00e9par\u00e9es par le serveur =<br \/>\nle plan mis en cache ne doit pas changer de type de r\u00e9sultat<\/a><\/noindex><\/p>\n<p><\/p>\n<p>Comment y rem\u00e9dier ? Il existe une recette simple : ne faites pas cela. Ne changez pas le search_path pendant l'ex\u00e9cution de l'application. Si vous le changez, mieux vaut cr\u00e9er une nouvelle connexion.<\/p>\n<p><\/p>\n<p>Nous pouvons en discuter, c'est-\u00e0-dire l'ouvrir, en discuter, compl\u00e9ter. Peut-\u00eatre convaincrons-nous les d\u00e9veloppeurs de la base de donn\u00e9es que, lorsque quelqu'un change une valeur, la base de donn\u00e9es devrait le signaler au client : \u00ab Regardez, votre valeur a \u00e9t\u00e9 mise \u00e0 jour. Peut-\u00eatre devez-vous r\u00e9initialiser les instructions, les recr\u00e9er ? \u00bb. Actuellement, la base de donn\u00e9es se comporte discr\u00e8tement et ne signale pas du tout que quelque chose a chang\u00e9 \u00e0 l'int\u00e9rieur des instructions. <\/p>\n<p><\/p>\n<p>Et je vais de nouveau insister - c'est quelque chose de peu typique pour Java. Nous verrons la m\u00eame chose dans PL\/pgSQL un pour un. Mais l\u00e0, elle sera reproduite.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL et JDBC, nous tirons le meilleur parti. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/a4e7b201d0c0aa4a0092d2b6ff152df7.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Essayons encore de s\u00e9lectionner des donn\u00e9es. Nous s\u00e9lectionnons, s\u00e9lectionnons. Nous avons une table d'un million de lignes. Chaque ligne fait un kilooctet. Environ un gigaoctet de donn\u00e9es. Et nous avons une m\u00e9moire vive de machine Java de 128 m\u00e9gaoctets. <\/p>\n<p><\/p>\n<p>Comme recommand\u00e9 dans tous les livres, nous utilisons le traitement par flux. C'est-\u00e0-dire que nous ouvrons resultSet et lisons les donn\u00e9es petit \u00e0 petit. Est-ce que cela fonctionnera ? Va-t-il planter par manque de m\u00e9moire ? Va-t-il lire un peu \u00e0 la fois ? Faisons confiance \u00e0 la base, faisons confiance \u00e0 Postgres. Nous ne faisons pas confiance. Va-t-on avoir un OutOfMemory ? Qui a d\u00e9j\u00e0 eu un OutOfMemory ? Et qui a r\u00e9ussi \u00e0 r\u00e9parer cela ? Quelqu'un a-t-il r\u00e9ussi \u00e0 r\u00e9cup\u00e9rer ? <\/p>\n<p><\/p>\n<p>Si vous avez un million de lignes, vous ne pouvez pas simplement s\u00e9lectionner ainsi. Il est imp\u00e9ratif d'utiliser OFFSET\/LIMIT. Qui est pour cette option ? Et qui est pour l'id\u00e9e de jouer avec autoCommit ? <\/p>\n<p><\/p>\n<p>Ici, comme d'habitude, la solution la plus inattendue s'av\u00e8re \u00eatre la bonne. Et si vous d\u00e9sactivez autoCommit, cela aidera. Pourquoi cela ? La science ne le sait pas. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL et JDBC, nous tirons le meilleur parti. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/2e5d9d2a8450a9069155de86a8ec387a.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Mais par d\u00e9faut, tous les clients se connectant \u00e0 la base de donn\u00e9es Postgres s\u00e9lectionnent toutes les donn\u00e9es. PgJDBC n'est pas une exception dans ce cas, il s\u00e9lectionne toutes les lignes.<\/p>\n<p><\/p>\n<p>Il existe une variation sur le th\u00e8me FetchSize, c'est-\u00e0-dire que l'on peut au niveau d'une instruction individuelle dire ici, s'il vous pla\u00eet, s\u00e9lectionnez les donn\u00e9es par 10, 50. Mais cela ne fonctionne pas tant que vous n'avez pas d\u00e9sactiv\u00e9 autoCommit. Vous avez d\u00e9sactiv\u00e9 autoCommit \u2013 cela commence \u00e0 fonctionner. <\/p>\n<p><\/p>\n<p>Mais parcourir le code et mettre setFetchSize partout n'est pas pratique. C'est pourquoi nous avons cr\u00e9\u00e9 un param\u00e8tre qui d\u00e9finit la valeur par d\u00e9faut pour toute la connexion.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL et JDBC, nous tirons le meilleur parti. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/c9ac8e483dc6bfdbbc300972ee1ab1e0.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Nous l'avons dit. Nous avons configur\u00e9 le param\u00e8tre. Et qu'est-ce que cela nous a donn\u00e9 ? Si nous choisissons peu de lignes, par exemple 10 lignes, notre surcharge est assez \u00e9lev\u00e9e. Il faut donc fixer cette valeur \u00e0 environ une centaine. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL et JDBC, nous tirons le meilleur parti. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/073e7a4af63688396eefe332ba8261d6.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Id\u00e9alement, bien s\u00fbr, il faudrait aussi apprendre \u00e0 limiter en octets, mais la recette est la suivante : fixez defaultRowFetchSize \u00e0 plus de cent et r\u00e9jouissez-vous. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL et JDBC, nous tirons le meilleur parti. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/e3078faf2078fcaec10d6d99872a9990.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Passons \u00e0 l'insertion de donn\u00e9es. L'insertion est plus simple, il existe diff\u00e9rentes options. Par exemple, INSERT, VALUES. C'est une bonne option. On peut dire 'INSERT SELECT'. En pratique, c'est la m\u00eame chose. Il n'y a pas de diff\u00e9rence de performance. <\/p>\n<p><\/p>\n<p>Les livres disent qu'il faut ex\u00e9cuter des Batch statements, les livres disent qu'on peut ex\u00e9cuter des commandes plus complexes avec plusieurs parenth\u00e8ses. Et dans Postgres, il y a une merveilleuse fonction \u2013 on peut faire COPY pour le rendre plus rapide. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL et JDBC, nous tirons le meilleur parti. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/4d281c9515bdf8de7fbf1e9922d192d0.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Si nous mesurons, nous pouvons faire plusieurs d\u00e9couvertes int\u00e9ressantes. Comment voulons-nous que cela fonctionne ? Nous voulons \u00e9viter de parser et de ne pas ex\u00e9cuter de commandes inutiles. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL et JDBC, nous tirons le meilleur parti. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/7b2e4343c51dab3ddb6919bf0f117c4d.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>En pratique, TCP ne nous permet pas de faire cela. Si le client est occup\u00e9 \u00e0 envoyer une requ\u00eate, la base de donn\u00e9es, en essayant de nous envoyer les r\u00e9ponses, ne lit pas les requ\u00eates. Au final, le client attend la base de donn\u00e9es, pendant que celle-ci attend que le client lise la r\u00e9ponse. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL et JDBC, nous tirons le meilleur parti. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/17a062c30ddd83cb890775ec1722044f.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>C'est pourquoi le client est contraint d'envoyer r\u00e9guli\u00e8rement un paquet de synchronisation. Interactions r\u00e9seau inutiles, perte de temps superflue.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL et JDBC, nous tirons le meilleur parti. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/ae7a4f32e2949c1d0550da051937de02.jpg\" style=\"display:block;margin: 0 auto;\" \/>Et plus nous en ajoutons, pire c'est. Le pilote est tr\u00e8s pessimiste et les ajoute assez souvent, environ toutes les 200 lignes, selon la taille des lignes, etc. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL et JDBC, nous tirons le meilleur parti. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/82b1d597986633a8c4b7952517ca6477.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/pgjdbc\/pgjdbc\/pull\/380\">https:\/\/github.com\/pgjdbc\/pgjdbc\/pull\/380<\/a><\/noindex><\/p>\n<p><\/p>\n<p>Il arrive que vous corrigiez une seule ligne et que tout s'acc\u00e9l\u00e8re dix fois. Cela arrive. Pourquoi ? Comme toujours, une constante avait d\u00e9j\u00e0 \u00e9t\u00e9 utilis\u00e9e quelque part. Et la valeur '128' signifiait - ne pas utiliser le batching.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL et JDBC, nous tirons le meilleur parti. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/7b6bcd95591b36441037b4c4631d2ad0.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p><noindex><a rel=\"nofollow\" href=\"http:\/\/openjdk.java.net\/projects\/code-tools\/jmh\/\">Java microbenchmark harness<\/a><\/noindex><\/p>\n<p><\/p>\n<p>C'est bien que cela ne soit pas tomb\u00e9 dans la version officielle. Nous l'avons d\u00e9couvert avant de commencer \u00e0 publier la version. Toutes les valeurs que je mentionne sont bas\u00e9es sur des versions r\u00e9centes. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL et JDBC, nous tirons le meilleur parti. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/28c82d9e11f6bdaf3cf62b49fc074d68.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Mesurons. Nous mesurons InsertBatch simple. Nous mesurons InsertBatch multiple, c'est-\u00e0-dire la m\u00eame chose, mais avec beaucoup de valeurs. Une man\u0153uvre astucieuse. Tout le monde ne sait pas faire cela, mais c'est un simple tour, bien plus simple que COPY.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL et JDBC, nous tirons le meilleur parti. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/72f7cf6c3b9d9175d3e5a6d5410fe209.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>On peut faire COPY.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL et JDBC, nous tirons le meilleur parti. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/2b0d8bbcc50f1293c9a8a1eb8eb70d2e.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Et vous pouvez le faire avec des structures. D\u00e9clarez le type User par d\u00e9faut, transmettez un tableau et ins\u00e9rez directement dans la table. <\/p>\n<p><\/p>\n<p>Si vous ouvrez le lien : pgjdbc\/ubenchmsrk\/InsertBatch.java, le code se trouve sur GitHub. Vous pouvez voir pr\u00e9cis\u00e9ment quelles requ\u00eates y sont g\u00e9n\u00e9r\u00e9es. Ce n'est pas le plus important.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL et JDBC, nous tirons le meilleur parti. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/28dede211fe401286685ca62081453de.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Nous avons lanc\u00e9. Et la premi\u00e8re chose que nous avons comprise, c'est qu'il est tout simplement impossible de ne pas utiliser le batch. Toutes les options de batching sont nulles, c'est-\u00e0-dire que le temps d'ex\u00e9cution est pratiquement \u00e9gal \u00e0 z\u00e9ro par rapport \u00e0 une ex\u00e9cution unique. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL et JDBC, nous tirons le meilleur parti. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/4ea68f712bbdeadd35f5baa4ddaeff33.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Nous ins\u00e9rons des donn\u00e9es. Il s'agit d'une table tr\u00e8s simple. Trois colonnes. Et que voyons-nous ici ? Nous voyons que ces trois options sont \u00e0 peu pr\u00e8s comparables. Et COPY, bien s\u00fbr, est le meilleur.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL et JDBC, nous tirons le meilleur parti. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/2686f1d7eea347a839ac23aef2a27e53.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>C'est quand nous ins\u00e9rons par morceaux. Quand nous avons dit, une valeur VALUES, deux valeurs VALUES, trois valeurs VALUES ou nous les avons sp\u00e9cifi\u00e9es 10 par des virgules. C'est exactement ce qui est maintenant horizontal. 1, 2, 4, 128. On voit que l'Insertion par lot, repr\u00e9sent\u00e9e en bleu, est nettement plus l\u00e9g\u00e8re. C'est-\u00e0-dire que lorsque vous ins\u00e9rez un par un ou m\u00eame quatre, \u00e7a devient deux fois mieux, simplement parce que nous avons mis un peu plus dans VALUES. Moins d'op\u00e9rations EXECUTE.<\/p>\n<p><\/p>\n<p>Utiliser COPY sur de petits volumes est extr\u00eamement peu prometteur. Je n'ai m\u00eame pas dessin\u00e9 sur les deux premiers. Ils vont vers les cieux, c'est-\u00e0-dire que ces chiffres verts pour COPY.<\/p>\n<p><\/p>\n<p>Il faut utiliser COPY quand vous avez au moins plus de cent lignes de donn\u00e9es. Les frais g\u00e9n\u00e9raux pour ouvrir cette connexion sont \u00e9lev\u00e9s. Et, honn\u00eatement, je ne me suis pas pench\u00e9 l\u00e0-dessus. J'ai optimis\u00e9 le batch, pas COPY. <\/p>\n<p><\/p>\n<p>Que faisons-nous ensuite ? Nous mesurons. Nous comprenons qu'il faut utiliser soit des structures, soit un batch astucieux combinant plusieurs valeurs. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL et JDBC, nous tirons le meilleur parti. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/e02fa2574e2d1b678382db15064610d3.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Que faut-il retenir de la pr\u00e9sentation d'aujourd'hui ?<\/p>\n<p><\/p>\n<ul>\n<li>PreparedStatement est notre essentiel. Cela apporte \u00e9norm\u00e9ment pour la performance. Cela laisse un grand seau de goudron. <\/li>\n<li>Et il faut faire un EXPLAIN ANALYZE 6 fois.<\/li>\n<li>Et il faut diluer OFFSET 0, et des astuces comme +0 pour corriger le pourcentage restant de nos requ\u00eates probl\u00e9matiques.<\/li>\n<\/ul>\n<p>Source : <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/post\/499794\/\">habr.com<\/a> <\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u041f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u044c\u0441\u044f \u0441 \u0440\u0430\u0441\u0448\u0438\u0444\u0440\u043e\u0432\u043a\u043e\u0439 \u0434\u043e\u043a\u043b\u0430\u0434\u0430 \u043d\u0430\u0447\u0430\u043b\u0430 2016 \u0433\u043e\u0434\u0430 \u0412\u043b\u0430\u0434\u0438\u043c\u0438\u0440\u0430 \u0421\u0438\u0442\u043d\u0438\u043a\u043e\u0432\u0430 &quot;PostgreSQL \u0438 JDBC \u0432\u044b\u0436\u0438\u043c\u0430\u0435\u043c \u0432\u0441\u0435 \u0441\u043e\u043a\u0438&quot; \u0414\u043e\u0431\u0440\u044b\u0439 \u0434\u0435\u043d\u044c! \u041c\u0435\u043d\u044f \u0437\u043e\u0432\u0443\u0442 \u0412\u043b\u0430\u0434\u0438\u043c\u0438\u0440 \u0421\u0438\u0442\u043d\u0438\u043a\u043e\u0432. \u042f \u0440\u0430\u0431\u043e\u0442\u0430\u044e 10 \u043b\u0435\u0442 \u0432 \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u0438 NetCracker. \u0418 \u0432 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u043c \u044f \u0437\u0430\u043d\u0438\u043c\u0430\u044e\u0441\u044c \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c\u044e. \u0412\u0441\u0435, \u0447\u0442\u043e \u0441\u0432\u044f\u0437\u0430\u043d\u043e \u0441 Java, \u0432\u0441\u0435, \u0447\u0442\u043e \u0441\u0432\u044f\u0437\u0430\u043d\u043e \u0441 SQL \u2013 \u044d\u0442\u043e \u0442\u043e, \u0447\u0442\u043e \u044f \u043b\u044e\u0431\u043b\u044e. \u0418 \u0441\u0435\u0433\u043e\u0434\u043d\u044f \u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443 [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":79894,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-79893","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.2 - aioseo.com -->\n\t<meta name=\"description\" content=\"\u041f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u044c\u0441\u044f \u0441 \u0440\u0430\u0441\u0448\u0438\u0444\u0440\u043e\u0432\u043a\u043e\u0439 \u0434\u043e\u043a\u043b\u0430\u0434\u0430 \u043d\u0430\u0447\u0430\u043b\u0430 2016 \u0433\u043e\u0434\u0430 \u0412\u043b\u0430\u0434\u0438\u043c\u0438\u0440\u0430 \u0421\u0438\u0442\u043d\u0438\u043a\u043e\u0432\u0430 &quot;PostgreSQL \u0438 JDBC \u0432\u044b\u0436\u0438\u043c\u0430\u0435\u043c \u0432\u0441\u0435 \u0441\u043e\u043a\u0438&quot;\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Yuri Gagarin\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/prohoster.info\/fr\/blog\/administrirovanie\/postgresql-i-jdbc-vyzhimaem-vse-soki-vladimir-sitnikov\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.2\" \/>\n\t\t<meta property=\"og:locale\" content=\"fr_FR\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"\ud83e\udd47PostgreSQL \u0438 JDBC \u0432\u044b\u0436\u0438\u043c\u0430\u0435\u043c \u0432\u0441\u0435 \u0441\u043e\u043a\u0438. \u0412\u043b\u0430\u0434\u0438\u043c\u0438\u0440 \u0421\u0438\u0442\u043d\u0438\u043a\u043e\u0432 | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u041f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u044c\u0441\u044f \u0441 \u0440\u0430\u0441\u0448\u0438\u0444\u0440\u043e\u0432\u043a\u043e\u0439 \u0434\u043e\u043a\u043b\u0430\u0434\u0430 \u043d\u0430\u0447\u0430\u043b\u0430 2016 \u0433\u043e\u0434\u0430 \u0412\u043b\u0430\u0434\u0438\u043c\u0438\u0440\u0430 \u0421\u0438\u0442\u043d\u0438\u043a\u043e\u0432\u0430 &quot;PostgreSQL \u0438 JDBC \u0432\u044b\u0436\u0438\u043c\u0430\u0435\u043c \u0432\u0441\u0435 \u0441\u043e\u043a\u0438&quot;\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/fr\/blog\/administrirovanie\/postgresql-i-jdbc-vyzhimaem-vse-soki-vladimir-sitnikov\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:width\" content=\"350\" \/>\n\t\t<meta property=\"og:image:height\" content=\"350\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2020-05-01T11:43:12+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2020-05-01T11:43:12+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"\ud83e\udd47Nous tirons le meilleur parti de PostgreSQL et JDBC. Vladimir Sitnikov | ProHoster","description":"Je vous propose de d\u00e9couvrir le compte rendu de la pr\u00e9sentation de d\u00e9but 2016 de Vladimir Sitnikov \"PostgreSQL et JDBC : tirons-en le meilleur parti\"","canonical_url":"https:\/\/prohoster.info\/fr\/blog\/administrirovanie\/postgresql-i-jdbc-vyzhimaem-vse-soki-vladimir-sitnikov","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"fr_FR","og:site_name":"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b","og:type":"article","og:title":"\ud83e\udd47PostgreSQL \u0438 JDBC \u0432\u044b\u0436\u0438\u043c\u0430\u0435\u043c \u0432\u0441\u0435 \u0441\u043e\u043a\u0438. \u0412\u043b\u0430\u0434\u0438\u043c\u0438\u0440 \u0421\u0438\u0442\u043d\u0438\u043a\u043e\u0432 | ProHoster","og:description":"\u041f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u044c\u0441\u044f \u0441 \u0440\u0430\u0441\u0448\u0438\u0444\u0440\u043e\u0432\u043a\u043e\u0439 \u0434\u043e\u043a\u043b\u0430\u0434\u0430 \u043d\u0430\u0447\u0430\u043b\u0430 2016 \u0433\u043e\u0434\u0430 \u0412\u043b\u0430\u0434\u0438\u043c\u0438\u0440\u0430 \u0421\u0438\u0442\u043d\u0438\u043a\u043e\u0432\u0430 &quot;PostgreSQL \u0438 JDBC \u0432\u044b\u0436\u0438\u043c\u0430\u0435\u043c \u0432\u0441\u0435 \u0441\u043e\u043a\u0438&quot;","og:url":"https:\/\/prohoster.info\/fr\/blog\/administrirovanie\/postgresql-i-jdbc-vyzhimaem-vse-soki-vladimir-sitnikov","og:image":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:secure_url":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:width":350,"og:image:height":350,"article:published_time":"2020-05-01T11:43:12+00:00","article:modified_time":"2020-05-01T11:43:12+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"79893","title":null,"description":null,"keywords":null,"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"seo_analyzer_scan_date":null,"breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-02-28 16:29:39","updated":"2022-10-10 00:32:43","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/posts\/79893","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/comments?post=79893"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/posts\/79893\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/media\/79894"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/media?parent=79893"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/categories?post=79893"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/fr\/wp-json\/wp\/v2\/tags?post=79893"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}