Si tu connais deja le fonctionnement d'hibernate la dessus, alors tu comprends la difference entre les 2, sinon, le pave ci dessous devrait te permettre de comprendre pourquoi le cache d'hibernate est tres efficace.
Meme si je suis convaincu que le cache d'oracle est tres efficace, ca implique quand meme d'empiler un appel jdbc, recuperer une connection, tres probablement passer par le reseau, taper oracle, va falloir qu'il compile son truc et te le retournes, re reseau, puis marshalling et pour enfin avoir ton objet.
En gros, ca veut dire:
Select foo, bar, toto from MaTable join MaCollectionAttribut where joincolum = id outer join MonAutrecollection where id = pw00t envoye a oracle.
Oracle va devoir parser tout ca, se dire qu'il a tout ca en memoire et que ca a pas ete modifie, et te retourner le contenu de son cache.
Retour a ton appli, tu te tapes tous tes:
MonObjetTable table = new MonObject()
table.setAttribut(new ObjetMachin()...);
MaCollection collection = new ArrayList()
blabla
Avec tous les tests d'integrite et tout le tralala, pour des objets qui n'ont au final pas ete modifie (ca s'trouve, ils sont meme immutables, tes objets...)
Si ton graphe d'objet est relativement costaud et que tes setters sont plus que de simples setters et font des trucs, ca fait potentiellement vachement de boulot.
Le 2nd level cache d'hibernate en revanche, contient tous tes objets java caches, grosso modo indexe par la PK.
Donc si ta requete est faite par la PK, ca va donner:
TonAppli fait une requete par PK,
Hibernate fait grosso modo un check:
if(monCachePourCetteClasse.containsKey(PK))
{
return monCachePourCetteClass.get(PK)
}
else
{
demander a oracle.
}
Dans la vraie vie, c'est un chtouille plus complexe que ca, parce qu'il faut gerer l'isolation, mais comme hibernate utilise des proxy a gogo, ca se gere sans recopie.
Ca te booste *tres sensiblement* les perfs, tu squeezes toute ta partie reseau, toute la partie SGBD et tout le "parsing"/allocation de ta reponse, tout ca c'est remplace par un getHashCode suivi d'un acces direct dans une table de hashage.
Pour quantifier, je m'amusais recemment a mesurer une requete serveur (ie, du point d'entree dans le remote facade jusqu'au return, c'est a dire de la requete marshalle jusqu'a juste avant la serialization de la reponse, en gros la seule partie sur laquelle tu peux accelerer les choses) passe de 0.75s a 0.1s entre la version cachee et pas cachee. La fonction en question, c'est uniquement un appel a la BD et return l'objet en question.
L'objet recupere etait tres touffu (grosso modo, un planning de visites en forme d'arbre, a la structure assez complexe) a cardinalite assez faible (45 visites, mais chaque visite a un gros paquet de machin complexe qui sont stockes dans d'autres tables).
Et en plus ca va t'economiser un gros paquet d'allocations potentiellement totalement inutiles.
Exemple tout con:
Je gard mon VisitSchedule, parce que c'est un bon exemple. Ton schedule contient une liste de Visit. Chaque Visit a lieu a une Location (4 ou 5 differentes dans le systeme), donc sans cache, ton impleme naive va te faire une allocation pour chaque Visit la ou tu pourrais partager une seule et meme instance.
C'est meme pire que ca en fait, a priori tu ne veux pas plusieurs allocation pour chaque Location, parce que tu vas potentiellement te retrouver avec un gros paquet d'instance d'une meme Location logique (== returns false but equals returns true), mais qui sont modifiees independament les unes des autres. Bon courage pour retrouver tes petits la dedans.
Resultat, tu te retrouve a devoir implementer un genre de cache si tu veux eviter de passer ton temps a demander a la db ce qu'il se passe et si tu veux avoir un graphe d'objet coherent en ram. Tu commences a avoir une couche DB qui a besoin de savoir comment fonctionne la couche metier, bref, ca devient un gros bordel a gerer.
Alors, apres, ce cache, il est pas magique non plus, tu peux te taper un retour de flamme tres violent.
Deja, ca ne marche que sur les requets par PK.
Genre select from chat where couleur = "noir" and cri="miaou" ira chez oracle quoi qu'il arrive. Et c'est la que tu profiteras du cache d'oracle :) Quand je te disais qu'il etait pas inutile.
Par contre select fromt chat where id = "45745715156874851ab47", ca ira dans le cache.
Ca implique aussi de faire assez gaffe a ce que tu fais dans ton code: Hibernate va faire une synchro cache => DB mais jamais dans l'autre sens. Si un objet est en cache, hibernate ne le rechargera jamais depuis la db (sauf si l'objet degage du cache, of course).
Ce que ca veut dire? C'est que si t'as une requete qui n'a pas vocation a persister quoi que ce soit, et que le dev se dit: bon, je vais pas me faire chier, je vais me permettre de prendre un raccourci ici et de laisser l'objet en peu en vrac, de toutes facons, c'est local, ben tu vas peter toute l'appli. Je l'avais perso sur un one to many bi directionnel que l'utilisateur pouvait modifier de chaque cote de l'association (region -> pays), je changeais la region d'un pays sans ajouter le pays a la region, en pensant betement qu'hibernate rechargerais le tout depuis la DB. grosse erreur, je me suis retrouve avec un pays qui affichait region bla et region bla qui ne contenait pas le pays.
Bref, dans ce cas: paf pasteque, et ce meme si ta DB est integre (bah vouais, en relationnel, ca marche tres bien ce que j'avais fait).
Dans le meme genre, t'as des effets de bords bizarre avec les sorts sur les collections aussi: si t'oublie de specifier un sort SQL, tu t'en rendras pas compte tant que ta collection a pas ete chargee depuis la DB.
Tu crees une liste cote serveur avec 1, 2, 3, tu persistes. Tu recharges, tu retrouves 1,2,3. Tu coupes le serveur, tu le relance, tu retrouve 2,3,1. Ca m'est arrive de mettre 2 mois a voir qu'un sort manquait.
Et bien evidemment, ca bouffe de la ram, faut bien stocker toute une palanquee de machin qui vont potentiellement jamais etre utilisee.
Mais bon, comme dit plus haut, tu sors pas ca pour un forum phpbb. Notre truc va tourner sur son serveur a lui rien que pour l'appli, donc on peut se permettre de goinfrer qq Go de ram pour notre cache.
[^] # Re: La différence principale entre php et c++
Posté par thedude . En réponse au journal On n'est pas vendredi et pourtant : impact environnemental de nos langages. Évalué à 4.
Si tu connais deja le fonctionnement d'hibernate la dessus, alors tu comprends la difference entre les 2, sinon, le pave ci dessous devrait te permettre de comprendre pourquoi le cache d'hibernate est tres efficace.
Meme si je suis convaincu que le cache d'oracle est tres efficace, ca implique quand meme d'empiler un appel jdbc, recuperer une connection, tres probablement passer par le reseau, taper oracle, va falloir qu'il compile son truc et te le retournes, re reseau, puis marshalling et pour enfin avoir ton objet.
En gros, ca veut dire:
Select foo, bar, toto from MaTable join MaCollectionAttribut where joincolum = id outer join MonAutrecollection where id = pw00t envoye a oracle.
Oracle va devoir parser tout ca, se dire qu'il a tout ca en memoire et que ca a pas ete modifie, et te retourner le contenu de son cache.
Retour a ton appli, tu te tapes tous tes:
MonObjetTable table = new MonObject()
table.setAttribut(new ObjetMachin()...);
MaCollection collection = new ArrayList()
blabla
Avec tous les tests d'integrite et tout le tralala, pour des objets qui n'ont au final pas ete modifie (ca s'trouve, ils sont meme immutables, tes objets...)
Si ton graphe d'objet est relativement costaud et que tes setters sont plus que de simples setters et font des trucs, ca fait potentiellement vachement de boulot.
Le 2nd level cache d'hibernate en revanche, contient tous tes objets java caches, grosso modo indexe par la PK.
Donc si ta requete est faite par la PK, ca va donner:
TonAppli fait une requete par PK,
Hibernate fait grosso modo un check:
if(monCachePourCetteClasse.containsKey(PK))
{
return monCachePourCetteClass.get(PK)
}
else
{
demander a oracle.
}
Dans la vraie vie, c'est un chtouille plus complexe que ca, parce qu'il faut gerer l'isolation, mais comme hibernate utilise des proxy a gogo, ca se gere sans recopie.
Ca te booste *tres sensiblement* les perfs, tu squeezes toute ta partie reseau, toute la partie SGBD et tout le "parsing"/allocation de ta reponse, tout ca c'est remplace par un getHashCode suivi d'un acces direct dans une table de hashage.
Pour quantifier, je m'amusais recemment a mesurer une requete serveur (ie, du point d'entree dans le remote facade jusqu'au return, c'est a dire de la requete marshalle jusqu'a juste avant la serialization de la reponse, en gros la seule partie sur laquelle tu peux accelerer les choses) passe de 0.75s a 0.1s entre la version cachee et pas cachee. La fonction en question, c'est uniquement un appel a la BD et return l'objet en question.
L'objet recupere etait tres touffu (grosso modo, un planning de visites en forme d'arbre, a la structure assez complexe) a cardinalite assez faible (45 visites, mais chaque visite a un gros paquet de machin complexe qui sont stockes dans d'autres tables).
Et en plus ca va t'economiser un gros paquet d'allocations potentiellement totalement inutiles.
Exemple tout con:
Je gard mon VisitSchedule, parce que c'est un bon exemple. Ton schedule contient une liste de Visit. Chaque Visit a lieu a une Location (4 ou 5 differentes dans le systeme), donc sans cache, ton impleme naive va te faire une allocation pour chaque Visit la ou tu pourrais partager une seule et meme instance.
C'est meme pire que ca en fait, a priori tu ne veux pas plusieurs allocation pour chaque Location, parce que tu vas potentiellement te retrouver avec un gros paquet d'instance d'une meme Location logique (== returns false but equals returns true), mais qui sont modifiees independament les unes des autres. Bon courage pour retrouver tes petits la dedans.
Resultat, tu te retrouve a devoir implementer un genre de cache si tu veux eviter de passer ton temps a demander a la db ce qu'il se passe et si tu veux avoir un graphe d'objet coherent en ram. Tu commences a avoir une couche DB qui a besoin de savoir comment fonctionne la couche metier, bref, ca devient un gros bordel a gerer.
Alors, apres, ce cache, il est pas magique non plus, tu peux te taper un retour de flamme tres violent.
Deja, ca ne marche que sur les requets par PK.
Genre select from chat where couleur = "noir" and cri="miaou" ira chez oracle quoi qu'il arrive. Et c'est la que tu profiteras du cache d'oracle :) Quand je te disais qu'il etait pas inutile.
Par contre select fromt chat where id = "45745715156874851ab47", ca ira dans le cache.
Ca implique aussi de faire assez gaffe a ce que tu fais dans ton code: Hibernate va faire une synchro cache => DB mais jamais dans l'autre sens. Si un objet est en cache, hibernate ne le rechargera jamais depuis la db (sauf si l'objet degage du cache, of course).
Ce que ca veut dire? C'est que si t'as une requete qui n'a pas vocation a persister quoi que ce soit, et que le dev se dit: bon, je vais pas me faire chier, je vais me permettre de prendre un raccourci ici et de laisser l'objet en peu en vrac, de toutes facons, c'est local, ben tu vas peter toute l'appli. Je l'avais perso sur un one to many bi directionnel que l'utilisateur pouvait modifier de chaque cote de l'association (region -> pays), je changeais la region d'un pays sans ajouter le pays a la region, en pensant betement qu'hibernate rechargerais le tout depuis la DB. grosse erreur, je me suis retrouve avec un pays qui affichait region bla et region bla qui ne contenait pas le pays.
Bref, dans ce cas: paf pasteque, et ce meme si ta DB est integre (bah vouais, en relationnel, ca marche tres bien ce que j'avais fait).
Dans le meme genre, t'as des effets de bords bizarre avec les sorts sur les collections aussi: si t'oublie de specifier un sort SQL, tu t'en rendras pas compte tant que ta collection a pas ete chargee depuis la DB.
Tu crees une liste cote serveur avec 1, 2, 3, tu persistes. Tu recharges, tu retrouves 1,2,3. Tu coupes le serveur, tu le relance, tu retrouve 2,3,1. Ca m'est arrive de mettre 2 mois a voir qu'un sort manquait.
Et bien evidemment, ca bouffe de la ram, faut bien stocker toute une palanquee de machin qui vont potentiellement jamais etre utilisee.
Mais bon, comme dit plus haut, tu sors pas ca pour un forum phpbb. Notre truc va tourner sur son serveur a lui rien que pour l'appli, donc on peut se permettre de goinfrer qq Go de ram pour notre cache.