kontakta oss


Det korta svaret: enligt det benchmark-test för paginering som Pagys skapare publicerat är Pagy med bred marginal det lättaste av de tre vanligaste Ruby on Rails-biblioteken. Vid rendering av samma navigering över samma poster allokerar det 184 objekt medan will_paginate allokerar cirka 3 200 och Kaminari cirka 6 400. För en applikation med tusentals samtidiga användare innebär den skillnaden att minnestaket per instans nås snabbare, vilket i sin tur påverkar hur många instanser du behöver betala för.
En sak att klargöra först, eftersom mycket av det som fortfarande skrivs om ämnet är föråldrat: Pagy är inte längre någon liten uppstickare. Det lanserades i februari 2018 och är numera ett moget bibliotek med stor spridning. Det som har ändrats är API:et. Pagy 43 var en total omdesign från grunden, så de flesta guider du hittar (inklusive våra egna, fram till nu) visar anrop som inte längre finns. Jag har byggt Rails-appar sedan version 0.8 och har använt alla tre biblioteken för paginering i produktion; den här artikeln är omskriven för att stämma överens med den nuvarande versionen.
Innan vi går in på jämförelsen, låt oss titta på underhållsläget, eftersom "vilken som är snabbast" spelar mindre roll än "vilken som fortfarande får support" när du ska låsa fast en kodbas i den under flera år.
| Gem | Nuvarande version | Senaste versionen | Underhållssignal |
|---|---|---|---|
| Pagy | 43.x | 2026 | Aktivt utvecklad; täta releaser; helt omdesignad i v43 |
| Kaminari | 1.2.2 | December 2021 | Fortfarande den i särklass mest installerade, men ingen release på över ett år |
| will_paginate | 4.0.1 | Juni 2024 | Officiellt i underhållsläge: inga nya funktioner, och själva README-filen hänvisar användare till alternativ |
Några saker framgår av den tabellen.
will_paginate är inte dött, men dess egen underhållare har satt punkt för det. Det fick stöd för Rails 7 i version 4.0 och en patch under 2024, så påståendet om att "senaste commit var 2017" som du ser i äldre inlägg (inklusive våra egna) är helt enkelt felaktigt. Den korrekta anledningen till att undvika det i nya projekt är inte brist på underhåll. Det är att underhållaren uttryckligen har satt det i underhållsläge och slutat lägga till funktioner. En rimlig grund för ett beslut; felaktig grund om du hänvisar till ett commit-datum från 2017.
Kaminari är den etablerade spelaren, och det betyder fortfarande en hel del. Det har den största installerade basen av de tre med bred marginal, en omfattande dokumentation, vy-mallar som du kan generera och anpassa, samt konfiguration per modell. Men dess senaste release var i december 2021. Det fungerar, det är stabilt, och på en adminpanel med låg trafik kommer ingen av prestandadiskussionerna nedan att märkas. Men momentum ligger helt klart någon annanstans.
Pagy är där den aktiva utvecklingsenergin finns. Utöver ren hastighet levereras det nu med flera pagineringstekniker tekniker i en och samma gem (offset, countless, keyset, keynav, calendar och search), plus rendering på server- eller klientsidan och interaktiva utvecklingsverktyg. Den bredden, inte bara minnesanvändningen, är en stor del av anledningen till att folk väljer det idag.
Antalet nya RubyGems som publicerades varje år ökade under början av 2010-talet, nådde sin topp runt 2015 och har sedan dess minskat. Det betyder inte att inget nytt är värt din uppmärksamhet. Det betyder att de genuint användbara nyheterna är mer sällsynta nu, och lättare att missa. Pagy var en av dem 2018; poängen med en uppdatering som denna är att "sällsynt och lätt att missa" även beskriver de förändringar i ett bra bibliotek när communityn väl har slutat skriva om det.
2010 till 2026
Verifierade siffror: 2014 (~27 000) och 2018 (~10 600 / ~29 per dag), från Infinums RubyGems-analyser.
Om du har arbetat med Rails tidigare har du förmodligen använt will_paginate eller Kaminari för att paginera indexsidor i en applikation. Paginering innebär att dela upp en lång lista med poster i numrerade sidor och rendera länkarna mellan dem. Om du är ny på Rails kommer du snart att behöva en lösning för paginering.
Pagy är ett pagineringsbibliotek byggt med prestanda som högsta prioritet, utan att vara krångligt att använda i nya eller befintliga applikationer. Det är ramverksoberoende: det fungerar med Rails såväl som med lättare Ruby-ramverk som Sinatra och Padrino, och det paginerar Active Record-relationer likväl som vanliga arrayer och andra samlingar.
Det var rimligt att vara skeptisk till ännu en paginerings-gem år 2018. Fragmentering är inte bra för öppen källkod, och några av de förbättringar som Pagy förde med sig föreslogs ursprungligen för Kaminari men avvisades med motiveringen att de skulle kräva omfattande ändringar i den gemens kärna. Det avvisandet är halva anledningen till att Pagy finns. Åtta år senare har skepsisen till största del lagt sig: antalet nedladdningar och stjärnor placerar Pagy stadigt bland de etablerade valen snarare än vid sidan av.
Se varje gem i en request-sökväg som bagage. Du bär med dig det på varje resa, oavsett om du öppnar det eller inte, och paginering är väskan som följer med på nästan varje listningssida i din produkt. En tung väska är inget problem på en enstaka resa. Det blir ett problem när du gör tiotusen resor i timmen.
Siffrorna nedan kommer från den benchmark-svit som publicerats av Pagys skapare, som renderar samma Bootstrap-paginering över samma poster med varje gem i tur och ordning.
Suiten registrerar två saker per gem: minnet som allokeras för att utföra arbetet, och antalet Ruby-objekt som allokeras. Den mäter inte databasfrågan bakom sidan, vilken är densamma oavsett vilket gem du använder. Den mäter inte heller slutanvändarens sidladdningstid, som domineras av frågan, vyn och nätverket. Och eftersom den är publicerad av skaparen av ett av de tre gemen bör den läsas som en jämförelse av pagineringsoverhead snarare än som en oberoende teststudie.
Två ärlighetsnoteringar om siffrorna nedan. För det första benchmarkar den publicerade sviten fortfarande äldre versioner (Pagy v3, will_paginate 3.1.7, Kaminari 1.1.1), så betrakta dem som overheaden för varje gems klassiska implementation. För det andra hävdar Pagys nuvarande v43 ytterligare förbättringar jämfört med sin egen v3-baslinje, så om något är det moderna gapet större, inte mindre. Siffrorna avser overheaden, och inget annat.
| Gem (testad version) | Allokerade objekt | Total allokerad minnesmängd | Minnesökning, 2 till 20 sidor |
|---|---|---|---|
| Pagy (v3.0.0) | 184 | ~19 KB | Platt (ingen mätbar ökning) |
| will_paginate (3.1.7) | ~3 198 | ~341 KB | ~2x |
| Kaminari (1.1.1) | ~6 445 | ~704 KB | ~4x |
Objektallokering är där skillnaden är som störst. Kaminari skapar ungefär 6 400 objekt för att rendera navigeringen; will_paginate omkring 3 200. Båda är tunga för uppgiften. Pagy gör det med 184.
Tillväxtkolumnen är lika viktig som totalsummorna. När du utökar pagineringsfältet från 2 till 20 länkar förblir Pagys minnesanvändning i princip oförändrad, medan will_paginate ungefär fördubblas och Kaminari ungefär fyrdubblas. På en välbesökt listningssida med ett brett sidfält betalas den kostnaden vid varje anrop.
Det finns också en aspekt av kodstorlek bakom minnesanvändningen. För att producera identiska resultat är Pagy ungefär 4 filer och ~140 rader; Kaminari omfattar ~36 filer, will_paginate ~23. Mindre kod som utför samma jobb innebär oftast färre allokeringar och mindre att hålla reda på, och här syns det tydligt i profileringen.
Samma navigering, samma poster. Lägre är bättre.
Ruby-objekt som allokerats för att rendera samma pagineringsutdata. Källa: Pagy-skaparens benchmark-svit (ddnexus/pagination-comparison), äldre gem-versioner. Pagy 43 hävdar ytterligare förbättringar jämfört med v3-referensen som visas här.
Varje allokerat objekt är minne som tas i anspråk och sedan måste återvinnas av skräpsamlaren (det bakgrundsarbete Ruby utför för att frigöra minne som inte längre används), och allt detta sker i en process som körs på de flesta listningssidor i produkten. Att minska allokeringen per anrop med en tiopotens gör inte en enskild sidladdning märkbart snabbare. Det ökar däremot hur många samtidiga anrop en instans kan hantera innan du behöver lägga till en till. För en infrastrukturkostnad som baseras på antal instanser är det där besparingen sker, och migreringen som möjliggör detta mäts i timmar snarare än i sprintar.
Vi ser detta mönster i vårt eget leveransarbete. När vi byggde om FlippedNormals, en marknadsplats för datorgrafik, kunde den gamla WordPress-plattformen inte längre hantera trafiken. Vi migrerade den därför till en skräddarsydd Ruby on Rails-applikation och flyttade den till AWS för en mer skalbar infrastruktur. Hela migreringen tog två månader och ökade trafiken med 4 % efter nylanseringen. Det är ett användbart exempel för denna diskussion just på grund av dess utformning: en katalog med över 28 000 produkter innebär att listnings-, sök- och bläddringssidor ligger på den kritiska sökvägen och anropas vid nästan varje förfrågan. Det är precis på dessa sidor som kostnaden per anrop för ett pagineringsbibliotek multipliceras med din trafik, och där valet som gjordes tidigt i processen i tysthet sätter taket för resurserna senare.
Detta medför en stor fördel: koden är genuint lättläst, vilket är anledningen till att författaren har kunnat profilera den rad för rad.
Paginering är ett exempel på ett bredare mönster, och det är just detta mönster vi letar efter när vi granskar en Rails-stack. Vi kallar det en resursgranskning av gems. Tre steg.
1. Inventera vad som körs vid varje anrop. Inte hela Gemfile, utan bara de gems som anropas på de sidor där du faktiskt har trafik: index- och listningssidor, sökresultat och instrumentpaneler. Paginering, serialisering, behörighetskontroller och view helpers hamnar oftast på den här listan. En gem som används en gång om dagen i ett bakgrundsjobb är inte värd att mäta.
2. Mät allokering, inte väntetid. Antalet objektallokeringar per anrop är det mått som bäst förutspår hur en applikation beter sig under hög belastning, och det är tillräckligt stabilt för att jämföra två olika implementationer. memory_profiler och benchmark-ips ger dig både allokering och tidsåtgång för en enskild åtgärd. Skillnaden mellan två gems för samma åtgärd är oftast uppenbar redan vid första körningen.
3. Modellera resultatet i instanser, inte megabyte. En besparing blir ett argument först när den uttrycks i form av kapacitetsutrymme: hur många fler samtidiga anrop en instans klarar av, och därmed hur många färre instanser samma trafikmängd kräver. Det är den siffran en teknisk beslutsfattare kan agera utifrån, och den som motiverar arbetet med en migrering.
De flesta Rails-applikationer bär på två eller tre sådana här onödiga laster. Paginering är det vi hittar oftast, eftersom det väljs tidigt, aldrig ses över och ligger precis på de sidor som får mest trafik.
Det är här äldre guider leder dig fel: Pagy::Backend / Pagy::Frontend includes, items: nyckelordet och pagy_nav hjälparen är alla från tiden före v43. Här är den nuvarande konfigurationen, som följer den officiella snabbstartsguiden.
1. Lägg till gemen, låst till minor-versionen (v43 använder ett hopp i versionsnumret, så en låsning till minor-versionen skyddar dig mot icke-bakåtkompatibla ändringar):
# Gemfile
gem 'pagy', '~> 43.6'Pagy 43 kräver Ruby 3.3 eller senare.
2. Inkludera den enda startpunktsmodulen i din applikationskontroller:
# app/controllers/application_controller.rb
include Pagy::MethodDen enda inkluderingen ersätter den gamla modulen med två delar (Backend plus Frontend) konfiguration. Metoder läses in automatiskt endast när de används.
3. Paginera i kontrollerns åtgärd. Välj en paginerare: :offset är det klassiska beteendet, :keyset är snabbast för stora sorterade samlingar:
# Offset pagination (the familiar page-number behaviour)
@pagy, @records = pagy(:offset, Product.all, limit: 20)
# Keyset pagination (fastest for large, ordered datasets)
@pagy, @records = pagy(:keyset, Product.order(:id).all, limit: 20)Notera limit:, som ersatte det gamla items: nyckelordet.
4. Rendera navigeringen i vyn. Hjälpmetoderna är nu metoder på @pagy objektet:
<%# Server-side navigation bar %>
<%== @pagy.series_nav %>
<%# Or a client-side, responsive variant, styled for Bootstrap or Bulma %>
<%== @pagy.series_nav_js(:bootstrap) %>
<%== @pagy.input_nav_js(:bulma) %>
<%# A "showing 476-500 of 1000" style summary %>
<%== @pagy.info_tag %>Det var hela snabbstarten. Om du vill anpassa appens tema interaktivt, eller ställa en fråga till Pagys inbyggda AI inifrån din egen app, aktiverar en enda rad i din layout head utvecklarverktygen:
<%== Pagy.dev_tools %>Flytten sker i controllern och vyn (filen som hanterar anropet och mallen som renderar sidan). Den officiella migreringsguiden delar upp det i några mekaniska steg, och i praktiken är det nästan en direkt mappning.
Controller. Äldre anrop översätts direkt:
# will_paginate
# @records = Product.some_scope.paginate(page: params[:page], per_page: 15)
# Kaminari
# @records = Product.some_scope.page(params[:page]).per(15)
# Pagy
@pagy, @records = pagy(:offset, Product.some_scope, limit: 15)Vy. Byt ut hjälparen:
<%# will_paginate: <%= will_paginate @records %> %>
<%# Kaminari: <%= paginate @records %> %>
<%== @pagy.series_nav %>Global konfiguration. Sök i din kodbas efter det gamla gem-namnet (WillPaginate, Kaminari) och ta bort dess initialiserare och eventuell monkey-patching. Ett globalt per_page blir Pagy::OPTIONS[:limit] i en Pagy-initialiserare, eller ett limit:per anrop. Om du förlitade dig på Kaminaris .page scope inuti modellmetoder, ta bort den där och använd paginatorn i kontrollern istället.
Kör sedan dina tester eller klicka dig igenom sidorna. Kvarvarande gammal kod kommer att orsaka fel, så ta bort den och försök igen tills applikationen renderas korrekt. Om du använde pagineringsstilar från Bootstrap eller Bulma fungerar den befintliga CSS-koden med Pagys series_nav hjälpare.
Pagy är inte en direkt ersättare: du kan inte bara byta ut gemen och lämna koden orörd. Hjälparnamnen skiljer sig åt, så varje paginerad vy måste besökas, och samlingen dekoreras inte längre med sidmetoder på det sätt som Kaminari dekorerar en Active Record-relation. All kod som anropar dessa metoder måste ändras. Pagys hjälpare returnerar råa HTML-strängar, vilket är precis det som gör dem snabba.
I en applikation med ett fåtal indexsidor är detta gjort på en eftermiddag. Om du har femtio sidor bör du planera det som ett avgränsat projekt och hantera det som vilken annan ändring som helst i kod som körs vid varje anrop: genom en granskning och med sidorna kontrollerade. Om du hellre vill lämna över arbetet är detta den typ av avgränsade uppdrag som våra Ruby on Rails-utvecklingstjänster täcker.
En ärlig rekommendation kräver också ett "när man bör låta bli".
Stanna kvar vid Kaminari när applikationen redan är djupt integrerad med det (du förlitar dig på dess genererade vymal och konfiguration per modell), eller när de paginerade sidorna har så låg trafik att minnesskillnaden aldrig kommer att synas på din faktura. Kaminari är moget, väldokumenterat och fortfarande det mest installerade av de tre. Argumentet för Pagy är ett resurs- argument, och det är som starkast när paginering ligger på en kritisk sökväg.
will_paginate är svårare att rekommendera för nya projekt, eftersom utvecklaren har stängt för nya funktioner. Men om du har en stabil app som redan använder det och inte har några prestandaproblem, är "underhållsläge" inte samma sak som "trasigt". Det finns ingen anledning att stressa med ett byte bara för sakens skull.
Resursargumentet för Pagy är befogat, men det är just ett argument, inte en lag. Anpassa valet efter din trafik.
Jag började med will_paginate eftersom det var det bästa verktyget vid den tidpunkten. Jag gick över till Kaminari några månader efter att det lanserades, och stannade där tills ett par veckor efter att Pagy dök upp.
Det som förvånade mig i efterhand var att jag hade bytt från will_paginate till Kaminari utan att ta hänsyn till prestanda överhuvudtaget, och i det publicerade benchmark-testet är Kaminari det tyngre av de två. Inga egna mätningar från min sida. Bara ren vana.
Det väcker en befogad fråga om resten av stacken: hur många bibliotek bär vi med oss som i tysthet skadar de applikationer vi designar och levererar, valda på samma sätt och aldrig utvärderade sedan dess? Det är frågan bakom granskningen ovan, och i vårt eget leveransarbete är det nu en stående punkt snarare än en eftertanke, eftersom kostnaden för att ställa frågan för sent är en infrastrukturfaktura som ingen kan förklara. Logiken sträcker sig långt bortom paginering, och långt bortom Rails. Ramverket du väljer sätter golvet. Biblioteken du kopplar på sätter taket.
Som en ansvarsfriskrivning: prestandasiffrorna ovan kommer från den benchmark-svit som publicerats av Pagys skapare (ddnexus/pagination-comparison), körd på de äldre versionerna av varje bibliotek.
Ja, i hög grad. Det har gått från att vara en nykomling till att bli ett av de vanligaste valen för paginering i Rails, med tiotals miljoner nedladdningar, tusentals stjärnor på GitHub och regelbundna releaser under 2026. Den aktiva utvecklingen på området sker hos Pagy; Kaminari har en större installerad bas men har inte släppt någon uppdatering sedan 2021.
Enligt författarens benchmark, ja, och med stor marginal när det gäller resursanvändning: Pagy allokerar cirka 184 objekt jämfört med Kaminari som allokerar cirka 6 400, och använder totalt sett betydligt mindre minne. Skillnaden som användaren märker vid en enskild sidladdning är liten. Skillnaden visar sig i hur många samtidiga förfrågningar en instans kan hantera.
Lägg till gem 'pagy', '~> 43.6', lägg till include Pagy::Method i din ApplicationController, ersätt varje .paginate(...) / .page(...).per(...) anrop med @pagy, @records = pagy(:offset, scope, limit: N), och ersätt vyn-hjälparen med <%== @pagy.series_nav %>Det är inte ett rakt utbyte, och varje paginerad vy måste besökas, men ändringen är begränsad och enkel att återställa.
Ja. Dess beräkningar körs på heltal snarare än Ruby-objekt, så kostnaden ökar inte med samlingens storlek. För mycket stora, sorterade tabeller, undviker Pagys :keyset paginerare den kostsamma djupaOFFSET -skanningen helt och hållet. Den gräns du till slut stöter på är databasens räknefråga, inte paginerings-gem:et, och det gäller för alla tre.
Pagy är ramverksoberoende. Det fungerar med Rails och med lättare ramverk som Sinatra och Padrino, och paginerar Active Record-relationer såväl som vanliga arrayer och andra samlingar. En ORM (objektrelationell mappning) är lagret som omvandlar databasrader till objekt; Active Record är den som är inbyggd i Rails, och Pagy arbetar vid sidan av den istället för att kopplas till dina modeller.
Det var en fullständig omdesign. Konfigurationen förenklades till ett enda include Pagy::Method, konfigurationen minskades dramatiskt och hjälpfunktionerna flyttades till @pagy -objektet (@pagy.series_nav istället för det gamla pagy_nav(@pagy)). Det lade även till nya paginerare (countish, keyset, keynav, calendar och search), klient-side rendering och interaktiva utvecklingsverktyg. Om du följer en äldre handledning, förvänta dig att metodnamnen skiljer sig åt.
På Imaginary Cloud arbetar vi med en bred teknikstack, inklusive Ruby on Rails. Om paginering utgör en kritisk del av din applikation, eller om du misstänker att andra gems kostar mer än de borde, kan vi utföra den resursrevision av gems som beskrivs ovan på din stack och identifiera var resursförbrukningen faktiskt ligger. Kontakta oss så går vi igenom det tillsammans.


VD på Imaginary Cloud och medförfattare till boken Product Design Process. Jag gillar mat, vin och Krav Maga (inte nödvändigtvis i den ordningen).
LinkedIn
People who read this post, also found these interesting: