2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
it 'not raise ActiveRecord::PreparedStatementCacheExpired' do
create(:user)
User.first
User.find_by_sql('ALTER TABLE users ADD new_metric_column integer;')
ActiveRecord::Base.transaction { User.first }
end
User.all
aktiivisen_tietueen mukaan Jäsennä sql-lauseeseenSen jälkeen lähetä se tietokantaan,Suorita ensin PREPARE Valmistetut lausunnot, SQL-lauseet jäsennetään, analysoidaan, optimoidaan ja kirjoitetaan uudelleen.Kun seurantaAnna EXECUTE-komento, laadittu lausunto suunnitellaan ja toteutetaan.pg_prepared_statements
helpottaaksesi seuraavan kerran, kun soitat vastaaviin lausuntoihin.suorittaa suoraan lausunnot jäsentämisen, analysoinnin ja optimoinnin sijaan, välttäen päällekkäistä työtä ja parantamalla tehokkuutta.User.first
User.all
# 执行上面的2个查询后,用connection.instance_variable_get(:@statements)就可以看到缓存的准备语句
ActiveRecord::Base.connection.instance_variable_get(:@statements)
==> <ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::StatementPool:0x00000001086b13c8
@cache={78368=>{""$user", public-SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT
$1"=>"a7", ""$user", public-SELECT "users".* FROM "users" /* loading for inspect */ LIMIT $1"=>"a8"}},
@statement_limit=1000, @connection=#<PG::Connection:0x00000001086b31a0>, @counter=8>
# 这个也可以看到,会在数据库中去查询
ActiveRecord::Base.connection.execute('select * from pg_prepared_statements').values
(0.5ms) select * from pg_prepared_statements
==> [["a7", "SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT $1", "2024-07-
11T07:03:06.891+00:00", "{bigint}", false], ["a8", "SELECT "users".* FROM "users" /* loading for inspect
*/ LIMIT $1", "2024-07-11T07:04:47.772+00:00", "{bigint}", false]]
Kuten seuraavassa esimerkissä, kun SELECT suoritetaan kenttien lisäämisen tai poistamisen jälkeen,pg tietokantaheittääcached plan must not change result type
, kiskoissaaktiivinen_tietueHanki tämä virhe ja heitä sittenActiveRecord::PreparedStatementCacheExpired
ALTER TABLE users ADD COLUMN new_column integer;
ALTER TABLE users DROP COLUMN old_column;
添加或删除列,然后执行 SELECT *
删除 old_column 列然后执行 SELECT users.old_column
cached plan must not change result type
virheexec_cache
Menetelmä, huomasin, että pg:n kiskojen virheenkäsittelymenetelmä on: raise ActiveRecord::PreparedStatementCacheExpired.new(e.cause.message)
prepare_statement
menetelmä valmisteltuun lausekkeen välimuistiinmodule ActiveRecord
module ConnectionHandling
def exec_cache(sql, name, binds)
materialize_transactions
mark_transaction_written_if_write(sql)
update_typemap_for_default_timezone
stmt_key = prepare_statement(sql, binds)
type_casted_binds = type_casted_binds(binds)
log(sql, name, binds, type_casted_binds, stmt_key) do
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
@connection.exec_prepared(stmt_key, type_casted_binds)
end
end
rescue ActiveRecord::StatementInvalid => e
raise unless is_cached_plan_failure?(e)
# Nothing we can do if we are in a transaction because all commands
# will raise InFailedSQLTransaction
if in_transaction?
raise ActiveRecord::PreparedStatementCacheExpired.new(e.cause.message)
else
@lock.synchronize do
# outside of transactions we can simply flush this query and retry
@statements.delete sql_key(sql)
end
retry
end
end
end
end
Rails6 ja uudemmat voivat poistaa tämän ominaisuuden käytöstä asettamalla tietokannan ready_statements arvoksi false.
default: &default
adapter: postgresql
encoding: unicode
prepared_statements: false
Rails6:ta ja vanhempia ei ole testattu. Jos yllä oleva ei toimi, voit yrittää luoda uuden alustustiedoston.
# config/initializers/disable_prepared_statements.rb:
db_configuration = ActiveRecord::Base.configurations[Rails.env]
db_configuration.merge!('prepared_statements' => false)
ActiveRecord::Base.establish_connection(db_configuration)
vahvista:
User.all
ActiveRecord::Base.connection.execute('select * from pg_prepared_statements').values
==> []
Johtopäätös: Pienissä projekteissa ei ole väliä, jos poistat tämän toiminnon käytöstä, ja suorituskyky säilyy lähes ennallaan. Kuitenkin suurissa projekteissa mitä enemmän käyttäjiä ja monimutkaisempia kyselylauseita, sitä suurempia etuja tämä toiminto tuo. joten voit päättää, poistatko sen käytöstä todellisen tilanteen mukaan.
select *
tullaselect id, name
Tällaiset erityiset kentät kiskoilla7Virallinen ratkaisuSe siitä# config/application.rb
module MyApp
class Application < Rails::Application
config.active_record.enumerate_columns_in_select_statements = true
end
end
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
#__fake_column__是自定义的,不要是某个表中的字段就行,如果是[:id],那么 User.all就会被解析为select name from users,没有id了
self.ignored_columns = [:__fake_column__]
end
Johtopäätös: Tämän ratkaisun ongelmana on, että kenttien lisääminen voidaan ratkaista täydellisesti, mutta kenttien poistaminen aiheuttaa silti virheitä. Esimerkiksi nimikentän poistamisen jälkeen, jos valmistetussa käskyssä olevaa nimeä ei ole olemassa, valitse id, name from users. Rails7:n virallisessa ratkaisussa on myös tämä ongelma
Johtopäätös: Sovelluksen uudelleenkäynnistys aiheuttaa tilapäisen palvelun 502 Ei saatavilla Tietenkin, kun sovellus otetaan käyttöön, palvelu on käynnistettävä uudelleen, ja myös 502 tulee näkyviin, joten on parasta ottaa käyttöön, kun kukaan ei käytä sitä (keskivaiheessa). yöllä?), jotta virheitä tulee mahdollisimman vähänPreparedStatementCacheExpired
Ilmoita virheestä
transaction
menetelmäclass ApplicationRecord < ActiveRecord::Base
class << self
def transaction(*args, &block)
retried ||= false
super
rescue ActiveRecord::PreparedStatementCacheExpired
if retried
raise
else
retried = true
retry
end
end
end
end
ApplicationRecord.transaction do ... end
taiMyModel.transaction
Johtopäätös: Tärkeä huomautus: Jos lähetät sähköposteja, kirjoitat sovellusliittymiin tai suoritat muita toimintoja, jotka ovat vuorovaikutuksessa ulkomaailman kanssa osana tapahtumaa, tämä saattaa aiheuttaa sen, että jotkin näistä toiminnoista toistuvat satunnaisesti kahdesti. Tästä syystä Rails ei virallisesti suorita uudelleenyrityksiä automaattisesti, vaan jättää sen sovelluskehittäjille.
>>>>>>>Kun testaan tätä menetelmää itse, saan silti virheitä.
ActiveRecord::Base.connection.clear_cache!
Täydellistä ratkaisua ei löytynyt