(function(w,d,s,l,i){w[l]=w[l]|||[];w[l].push({'gtm.start': new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0], j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=? 'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f); })(window,document,'script','dataLayer','GTM-5LHNRP9'); GraphQL Ruby. Kā ir ar veiktspēju? - The Codest
The Codest
  • Par mums
  • Pakalpojumi
    • Programmatūras izstrāde
      • Frontend izveide
      • Backend izstrāde
    • Staff Augmentation
      • Frontend izstrādātāji
      • Backend izstrādātāji
      • Datu inženieri
      • Mākoņa inženieri
      • QA inženieri
      • Citi
    • Tā Konsultatīvais dienests
      • Audits un konsultācijas
  • Nozares
    • Fintech un banku darbība
    • E-commerce
    • Adtech
    • Healthtech
    • Ražošana
    • Loģistika
    • Automobiļu nozare
    • IOT
  • Vērtība par
    • CEO
    • CTO
    • Piegādes vadītājs
  • Mūsu komanda
  • Case Studies
  • Zināt, kā
    • Blogs
    • Tikšanās
    • Tiešsaistes semināri
    • Resursi
Karjera Sazinieties ar mums
  • Par mums
  • Pakalpojumi
    • Programmatūras izstrāde
      • Frontend izveide
      • Backend izstrāde
    • Staff Augmentation
      • Frontend izstrādātāji
      • Backend izstrādātāji
      • Datu inženieri
      • Mākoņa inženieri
      • QA inženieri
      • Citi
    • Tā Konsultatīvais dienests
      • Audits un konsultācijas
  • Vērtība par
    • CEO
    • CTO
    • Piegādes vadītājs
  • Mūsu komanda
  • Case Studies
  • Zināt, kā
    • Blogs
    • Tikšanās
    • Tiešsaistes semināri
    • Resursi
Karjera Sazinieties ar mums
Atpakaļ bultiņa ATGRIEZTIES ATPAKAĻ
2019-10-08
Programmatūras izstrāde

GraphQL Ruby. Kā ir ar veiktspēju?

The Codest

Tomasz Szkaradek

Attīstības arhitekts

GraphQL, tāpat kā jebkurai citai tehnoloģijai, ir savas problēmas, no kurām dažas tieši izriet no arhitektūras, bet dažas ir identiskas tām, kas sastopamas jebkurā citā lietojumprogrammā. Tomēr risinājumi ir pilnīgi atšķirīgi.

Lai parādītu problēmu, pieņemsim šādu lietojumprogrammas arhitektūru:

https://drive.google.com/file/d/1N4sWPJSls0S8FFHbpHCUVHBNBpEuSsyz/view

Un šeit atbilstošais vaicājums GraphQL lai lejupielādētu dati. Mēs iegūstam visas saites, kā arī sistēmā pievienoto plakātu un tā saites,

{
  allLinks {
    id
    url
    apraksts
    createdAt
    postBy {
      id
      nosaukums
      saites {
        id
      }
    }
  }
}

Kā parādīts tālāk, šeit redzama klasiskā n + 1 problēma ar attiecībām.

Saites ielāde (0,4ms) SELECT "links".* FROM "links" ORDER BY created_at DESC
  ↳ app/controllers/graphql_controller.rb:5:in `execute'
  Lietotāja ielāde (0.3ms) SELECT "users".* FROM "users" WHERE "users". "id" = ? LIMIT ?  [[["id", 40], ["LIMIT", 1]]]
  ↳ app/controllers/graphql_controller.rb:5:in `execute'
  Saites ielāde (0,3 ms) SELECT "links".* FROM "links" WHERE "links". "user_id" = ?  [[["user_id", 40]]]
  ↳ app/controllers/graphql_controller.rb:5:in `execute'
  Lietotāja ielāde (0.1ms) SELECT "users".* FROM "users" WHERE "users". "id" = ? LIMIT ?  [[["id", 38], ["LIMIT", 1]]]
  ↳ app/controllers/graphql_controller.rb:5:in `execute'
  Saites ielāde (0.1ms) SELECT "links".* FROM "links" WHERE "links". "user_id" = ?  [[["user_id", 38]]]
  ↳ app/controllers/graphql_controller.rb:5:in `execute'
  Lietotāja ielāde (0.2ms) SELECT "users".* FROM "users" WHERE "users". "id" = ? LIMIT ?  [[["id", 36], ["LIMIT", 1]]]
  ↳ app/controllers/graphql_controller.rb:5:in `execute'
  Saites ielāde (0.1ms) SELECT "links".* FROM "links" WHERE "links". "user_id" = ?  [[["user_id", 36]]]
  ↳ app/controllers/graphql_controller.rb:5:in `execute'
  Lietotāja ielāde (0.1ms) SELECT "users".* FROM "users" WHERE "users". "id" = ? LIMIT ?  [[["id", 34], ["LIMIT", 1]]]
  ↳ app/controllers/graphql_controller.rb:5:in `execute'
  Saites ielāde (0,2 ms) SELECT "links".* FROM "links" WHERE "links". "user_id" = ?  [[["user_id", 34]]]
  ↳ app/controllers/graphql_controller.rb:5:in `execute'
  Lietotāja ielāde (0.1ms) SELECT "users".* FROM "users" WHERE "users". "id" = ? LIMIT ?  [[["id", 32], ["LIMIT", 1]]]

Šajā gadījumā tas darbojas tieši tāpat kā šis gabals kods:
Link.all.map(&:user).map(&:links).

Šķiet, ka mēs zinām problēmas risinājumu: Link.includes(user: :links).map(&:user).map(&:links), bet vai tas patiešām darbosies? Pārbaudīsim to!

Lai pārliecinātos par labojumu, es mainīju GraphQL vaicājumu, lai izmantotu tikai dažus laukus un nekādas saistības.

{
  allLinks {
    id
    url
    apraksts
    createdAt
  }
}

Diemžēl rezultāts rāda, ka, neraugoties uz to, ka trūkst saikņu saistībā ar lietotāju un tā saitēm, mēs joprojām pievienojam šos datus datubāzes vaicājumam. Diemžēl tie ir lieki, un ar vēl sarežģītāku struktūru tas izrādās vienkārši neefektīvi.

GraphqlController#execute apstrāde kā */*
  Parametri: {"query"=>"{n allLinks {n idn urln descriptionn createdAtn }n}", "graphql"=>{{"query"=>"{n allLinks {n idn urln descriptionn createdAtn }n}"}}}
  Saites ielāde (0,3 ms) SELECT "links".* FROM "links" ORDER BY created_at DESC
  ↳ app/controllers/graphql_controller.rb:5:in `execute'
  Lietotāja ielāde (0.3ms) SELECT "users".* FROM "users" WHERE "users". "id" IN (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) [[["id", 40], ["id", 38], ["id", 36], ["id", 34], ["id", 32], ["id", 30], ["id", 28], ["id", 26], ["id", 24], ["id", 22], ["id", 20], ["id", 18], ["id", 16], ["id", 14], ["id", 12], ["id", 10], ["id", 8], ["id", 6], ["id", 4], ["id", 2]].
  ↳ app/controllers/graphql_controller.rb:5:in `execute'
  Saites ielāde (0.3ms) SELECT "links".* FROM "links" WHERE "links". "user_id" IN (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) [[["user_id", 2], ["user_id", 4], ["user_id", 6], ["user_id", 8], ["user_id", 10], ["user_id", 12], ["user_id", 14], ["user_id", 16], ["user_id", 18], ["user_id", 20], ["user_id", 22], ["user_id", 24], ["user_id", 26], ["user_id", 28], ["user_id", 30], ["user_id", 32], ["user_id", 34], ["user_id", 36], ["user_id", 38], ["user_id", 40]].
  ↳ app/controllers/graphql_controller.rb:5:in `execute'
Pabeigts 200 OK pēc 39 ms (skatījumi: 0,7 ms | ActiveRecord: 0,9 ms | piešķīrumi: 8730)

In GraphQL, šādas problēmas tiek risinātas citādi,vienkārši ielādējot datus partijās, pieņemot, ka dati ir nepieciešami, kad tie tiek ievadīti vaicājumā. Tā ir šāda slinka ielāde. Viena no populārākajām bibliotēkām ir https://github.com/Shopify/graphql-batch/.

Diemžēl tā uzstādīšana nav tik ērta, kā varētu šķist. Datu ielādētāji ir pieejami šeit: https://github.com/Shopify/graphql-batch/tree/master/examples, es domāju. RecordLoader klases unAssociationLoader klase. Ļaujiet mums klasiski instalēt gem 'graphql-batch' bibliotēku un pēc tam pievienot to mūsu shēmai, kā arī ielādētājus:

# graphql-ruby/app/graphql/graphql_tutorial_schema.rb
klase GraphqlTutorialSchema < GraphQL::Schema
  query Types::QueryType
  mutation Types::MutationType
  use GraphQL::Batch
  ...
end

Un mūsu veidi:

# graphql-ruby/app/graphql/types/link_type.rb
modulis Types
  klase LinkType < BaseNode
    field :created_at, DateTimeType, null: false
    field :url, String, null: false
    field :description, String, null: false
    lauks :post_by, UserType, null: false, metode: :user
    lauks :votes, [Types::VoteType], null: false

    def user
      Loaders::RecordLoader.for(User).load(object.user_id)
    end
  end
end

# graphql-ruby/app/graphql/types/user_type.rb
modulis Types
  klase UserType < BaseNode
    field :created_at, DateTimeType, null: false
    field :name, String, null: false
    lauks :email, String, null: false
    lauks :votes, [VoteType], null: false
    lauks :links, [LinkType], null: false

    def links
      Loaders::AssociationLoader.for(User, :links).load(object)
    end
  end
end

Izmantojot iekrāvēji, mēs veicam datu paketi un meklējam datus divos vienkāršos sql vaicājumos:

Sākts POST "/graphql" ::1 2021-06-16 22:40:17 +0200
   (0.1ms) SELECT sqlite_version(*)
Apstrāde ar GraphqlController#execute kā */*
  Parametri: {"query"=>"{n allLinks {n idn urln descriptionn createdAtn postBy {n idn named links {n idn }n }n }n }n}", "graphql"=>{{"query"=>"{n allLinks {n idn urln descriptionn createdAtn postBy {n idn named links {n idn }n }n }n}"}}}
  Saites ielāde (0.4ms) SELECT "links".* FROM "links"
  ↳ app/controllers/graphql_controller.rb:5:in `execute'
  Lietotāju ielāde (0.9ms) SELECT "users".* FROM "users" WHERE "users". "id" IN (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) [[["id", 2], ["id", 4], ["id", 6], ["id", 8], ["id", 10], ["id", 12], ["id", 14], ["id", 16], ["id", 18], ["id", 20], ["id", 22], ["id", 24], ["id", 26], ["id", 28], ["id", 30], ["id", 32], ["id", 34], ["id", 36], ["id", 38], ["id", 40]].
  ↳ app/graphql/loaders/record_loader.rb:12:in `perform'
  Saites ielāde (0,5ms) SELECT "links".* FROM "links" WHERE "links". "user_id" IN (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) [[["user_id", 2], ["user_id", 4], ["user_id", 6], ["user_id", 8], ["user_id", 10], ["user_id", 12], ["user_id", 14], ["user_id", 16], ["user_id", 18], ["user_id", 20], ["user_id", 22], ["user_id", 24], ["user_id", 26], ["user_id", 28], ["user_id", 30], ["user_id", 32], ["user_id", 34], ["user_id", 36], ["user_id", 38], ["user_id", 40]].
  ↳ app/graphql/loaders/association_loader.rb:46:in `preload_association'
Pabeigts 200 OK pēc 62 ms (skatījumi: 1,3 ms | ActiveRecord: 1,8 ms | piešķīrumi: 39887)

Ir arī citi risinājumi, kas atrisina šo problēmu, piemēram:

https://github.com/exAspArk/batch-loader#basic-example

Pieprasījumu sarežģītība

N + 1 vaicājumi nav viss, jo GraphQL mēs varam brīvi pārnest uz nākamajiem atribūtiem. Pēc noklusējuma tas ir iestatīts uz 1. Tas dažkārt serverim var būt pārāk daudz, īpaši situācijā, kad varam brīvi ievietot datus. Kā ar to tikt galā? Mēs varam ierobežot vaicājuma sarežģītību, bet, lai to izdarītu, mums atribūtos jānorāda arī to izmaksas. Pēc noklusējuma tā ir iestatīta uz 1. Mēs iestatām šīs izmaksas, izmantojot sarežģītība: atribūtu, kurā varam ievadīt datus: lauks: links, [LinkType], null: false, sarežģītība: 101. Lai ierobežošana patiešām darbotos, jūsu shēmā joprojām ir jāievieš maksimālais ierobežojums:

klase GraphqlTutorialSchema < GraphQL::Schema
  query Types::QueryType
  mutation Types::MutationType
  use GraphQL::Batch
  max_complexity 100
  ...
beigas

Izsekošana

GraphQL pieprasījumus apstrādā atšķirīgi, un izsekošana nav tik vienkārša, ja salīdzina ar to, ko mēs varam izdarīt lokāli. Diemžēl plaukts mini profilētājs vai parasts SQL žurnāls nepastāstīs. mums visu un nenorādīs, kura vaicājuma daļa ir atbildīga par konkrēto laika nogriezni. GraphQL-Ruby gadījumā mēs varam izmantot šeit pieejamos komerciālos risinājumus: https://graphql-ruby.org/queries/tracing, vai mēģināt sagatavot savu izsekojamību. Zemāk dotais fragments izskatās kā vietējais trasētājs.

# lib/my_custom_tracer.rb
klase MyCustomTracer  'graphql.lex',
    'parse' => 'graphql.parse',
    'validate' => 'graphql.validate',
    'analyze_query' => 'graphql.analyze_query',
    'analyze_multiplex' => 'graphql.analyze_multiplex',
    'execute_multiplex' => 'graphql.execute_multiplex',
    'execute_query' => 'graphql.execute_query',
    'execute_query_lazy' => 'graphql.execute_query_lazy'
  }

  def platform_trace(platform_key, key, _data, &block)
    start = ::Process.clock_gettime ::Process::CLOCK_MONOTONIC
    result = block.call
    ilgums = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC) - start
    observe(platform_key, key, duration)
    result
  beigas

  def platform_field_key(type, field)
    "graphql.#{type.graphql_name}.#{field.graphql_name}"
  end

  def platform_authorized_key(type)
    "graphql.authorized.#{type.graphql_name}"
  end

  def platform_resolve_type_key(type)
    "graphql.resolve_type.#{type.graphql_name}"
  end

  def observe(platform_key, key, duration)
    return if key == 'authorized'

    puts "platform_key: #{platform_key}, key: #{key}, duration: #{(ilgums * 1000).round(5)} ms".yellow
  beigas
beigas

Uzstādīšana ir arī ļoti vienkārša, jums ir jāiekļauj trasēšanas informācija shēmā. trasētājs (MyCustomTracer.new) konfigurācija. Kā tālāk dotajā piemērā:

# graphql-ruby/app/graphql/graphql_tutorial_schema.rb
klase GraphqlTutorialSchema < GraphQL::Schema
  query Types::QueryType
  mutation Types::MutationType
  use GraphQL::Batch
  tracer(MyCustomTracer.new)
  ...
beigas

Šādas izsekošanas rezultāts izskatās šādi:

Sākts POST "/graphql" ::1 2021-06-17 22:02:44 +0200
   (0.1ms) SELECT sqlite_version(*)
Apstrāde ar GraphqlController#execute kā */*
  Parametri: {"query"=>"{n allLinks {n idn urln descriptionn createdAtn postBy {n idn named links {n idn }n }n }n }n}", "graphql"=>{{"query"=>"{n allLinks {n idn urln descriptionn createdAtn postBy {n idn named links {n idn }n }n }n}"}}}
platform_key: graphql.lex, key: lex, duration: 0.156 ms
platform_key: graphql.parse, atslēga: parse, ilgums: 0.108 ms
platform_key: graphql.validate, atslēga: validate, ilgums: 0.537 ms
platform_key: graphql.analyze_query, atslēga: analyze_query, ilgums: 0.123 ms
platform_key: graphql.analyze_multiplex, atslēga: analyze_multiplex, ilgums: 0.159 ms
  Saites ielāde (0,4 ms) SELECT "links".* FROM "links"
  ↳ app/graphqql/graphql_tutorial_schema.rb:21:in `platform_trace'
platform_key: graphql.execute_query, key: execute_query, duration: 15.562 ms
  ↳ app/graphql/loaders/record_loader.rb:12:in `perform'
  ↳ app/graphql/loaders/association_loader.rb:46:in `preload_association'
platform_key: graphql.execute_query_lazy, key: execute_query_lazy, duration: 14.12 ms
platform_key: graphql.execute_multiplex, key: execute_multiplex, ilgums: 31.11 ms
200 OK pabeigts 48 ms laikā (skatījumi: 1,2 ms | ActiveRecord: 2,0 ms | piešķīrumi: 40128).

Kopsavilkums

GraphQL vairs nav jauna tehnoloģija, taču tās problēmu risinājumi nav pilnībā standartizēti, ja tie nav daļa no bibliotēkas. Šīs tehnoloģijas ieviešana projekts sniedz daudz iespēju mijiedarboties ar frontend, un es personīgi uzskatu, ka tā ir jauna kvalitāte salīdzinājumā ar to, ko REST API piedāvājumi.

Kāpēc jums (iespējams) vajadzētu izmantot Typescript

Kā nenogalināt projektu ar sliktu kodēšanas praksi?

Datu iegūšanas stratēģijas NextJS

Saistītie raksti

Ilustrācija viedtālruņa veselības aprūpes lietotnei ar sirds ikonu un pieaugošo veselības diagrammu, kas apzīmēta ar The Codest logotipu, kurš pārstāv digitālās veselības un HealthTech risinājumus.
Programmatūras izstrāde

Veselības aprūpes programmatūra: Mārketinga programmatūra: veidi, izmantošanas gadījumi

Šodien veselības aprūpes organizāciju rīcībā esošie rīki vairs neatgādina papīra diagrammas, kas tika izmantotas pirms vairākiem gadu desmitiem. veselības aprūpes programmatūra tagad atbalsta veselības aprūpes sistēmas, pacientu aprūpi un mūsdienīgu veselības aprūpes sniegšanu klīniskajās un...

TĀKĀDĒJAIS
Abstrakta ilustrācija ar lejupejošu joslu diagrammu ar augošu bultiņu un zelta monētu, kas simbolizē izmaksu efektivitāti vai ietaupījumus. Augšējā kreisajā stūrī redzams The Codest logotips ar saukli "In Code We Trust" uz gaiši pelēka fona.
Programmatūras izstrāde

Kā paplašināt izstrādātāju komandu, nezaudējot produkta kvalitāti

Palielināt izstrādātāju komandu? Uzziniet, kā augt, nezaudējot produkta kvalitāti. Šajā rokasgrāmatā aplūkotas pazīmes, kas liecina, ka ir pienācis laiks paplašināt komandu, komandas struktūra, pieņemšana darbā, vadība un rīki, kā arī tas, kā The Codest var...

TĀKĀDĒJAIS
Programmatūras izstrāde

Uz nākotni noturīgu tīmekļa lietojumprogrammu veidošana: The Codest ekspertu komandas ieskats

Uzziniet, kā The Codest izceļas mērogojamu, interaktīvu tīmekļa lietojumprogrammu izveidē, izmantojot modernākās tehnoloģijas un nodrošinot viengabalainu lietotāja pieredzi visās platformās. Uzziniet, kā mūsu zināšanas veicina digitālo transformāciju un biznesa...

TĀKĀDĒJAIS
Programmatūras izstrāde

Top 10 Latvijā bāzēti programmatūras izstrādes uzņēmumi

Mūsu jaunākajā rakstā uzziniet vairāk par Latvijas labākajiem programmatūras izstrādes uzņēmumiem un to inovatīvajiem risinājumiem. Uzziniet, kā šie tehnoloģiju līderi var palīdzēt uzlabot jūsu biznesu.

thecodest
Uzņēmumu un mērogošanas risinājumi

Java programmatūras izstrādes pamati: A Guide to Outsourcing Successfully

Izpētiet šo būtisko rokasgrāmatu par veiksmīgu outsourcing Java programmatūras izstrādi, lai uzlabotu efektivitāti, piekļūtu speciālajām zināšanām un sekmīgi īstenotu projektus ar The Codest.

thecodest

Abonējiet mūsu zināšanu bāzi un saņemiet jaunāko informāciju par IT nozares pieredzi.

    Par mums

    The Codest - starptautisks programmatūras izstrādes uzņēmums ar tehnoloģiju centriem Polijā.

    Apvienotā Karaliste - Galvenā mītne

    • 303B birojs, 182-184 High Street North E6 2JA
      Londona, Anglija

    Polija - Vietējie tehnoloģiju centri

    • Fabryczna Office Park, Aleja
      Pokoju 18, 31-564 Krakova
    • Brain Embassy, Konstruktorska
      11, 02-673 Varšava, Polija

    The Codest

    • Sākums
    • Par mums
    • Pakalpojumi
    • Case Studies
    • Zināt, kā
    • Karjera
    • Vārdnīca

    Pakalpojumi

    • Tā Konsultatīvais dienests
    • Programmatūras izstrāde
    • Backend izstrāde
    • Frontend izveide
    • Staff Augmentation
    • Backend izstrādātāji
    • Mākoņa inženieri
    • Datu inženieri
    • Citi
    • QA inženieri

    Resursi

    • Fakti un mīti par sadarbību ar ārējo programmatūras izstrādes partneri
    • No ASV uz Eiropu: Kāpēc Amerikas jaunuzņēmumi nolemj pārcelties uz Eiropu?
    • Tehnoloģiju ārzonas attīstības centru salīdzinājums: Tech Offshore Eiropa (Polija), ASEAN (Filipīnas), Eirāzija (Turcija)
    • Kādi ir galvenie CTO un CIO izaicinājumi?
    • The Codest
    • The Codest
    • The Codest
    • Privacy policy
    • Website terms of use

    Autortiesības © 2026 The Codest. Visas tiesības aizsargātas.

    lvLatvian
    en_USEnglish de_DEGerman sv_SESwedish da_DKDanish nb_NONorwegian fiFinnish fr_FRFrench pl_PLPolish arArabic it_ITItalian es_ESSpanish nl_NLDutch etEstonian elGreek pt_PTPortuguese cs_CZCzech lt_LTLithuanian is_ISIcelandic lvLatvian