Panduan Pasukan Lucee MY

EHCache dan memcached sebagai cache Lucee

Panduan cache Lucee: daftar EHCache dan memcached dalam Application.cfc, tetapan eternal dan TTL, corak cache selamat, dan ukuran hit ratio serta masa respons.

EHCache dan memcached sebagai cache Lucee
0
Kandungan

EHCache dan memcached sebagai cache Lucee

Cache dalam Lucee bukan satu kotak tunggal, ia berlapis tiga: template yang sudah dikompil, hasil query yang berulang, dan objek yang anda simpan sendiri. Dua enjin yang paling kerap disebut ialah EHCache dan memcached, dan kedua-duanya menyelesaikan masalah berbeza. EHCache hidup dalam JVM yang sama dengan Lucee, jadi pantas tetapi terikat pada satu pelayan; memcached hidup dalam proses berasingan, jadi boleh dikongsi banyak pelayan. Artikel ini menunjukkan cara mendaftar EHCache, tetapan yang wujud, corak selamat, dan cara mengukur sama ada cache membantu.

Tiga lapisan cache dalam Lucee

Lucee membahagikan cache mengikut jenis data, dan setiap jenis boleh dihalakan ke enjin berbeza. Tiga lapisan utama ialah template, query dan objek.

Cache template paling automatik: Lucee mengkompil fail .cfm dan .cfc kepada Java bytecode dan menyimpan hasilnya.

Cache query menyimpan hasil SQL. Dengan cachedwithin pada cfquery atau queryExecute, Lucee menghash SQL, parameter dan nama datasource, lalu memulangkan hasil tersimpan untuk panggilan sama dalam tempoh itu. Sesuai untuk data yang dibaca banyak tetapi jarang berubah.

Cache objek paling fleksibel: anda simpan apa sahaja di bawah kunci pilihan anda dengan cachePut, dan baca semula dengan cacheGet. Lapisan ini paling kerap disalahgunakan apabila kunci dan TTL tidak dirancang.

Selain itu Lucee juga ada cache untuk fungsi dan hasil cfhttp, tetapi tiga di atas paling menentukan. Pilihan enjin dibincangkan dalam panduan caching untuk prestasi.

Mendaftar EHCache dalam Application.cfc

Dalam Lucee 7, EHCache bukan sebahagian teras, ia sambungan org.lucee:ehcache-extension dengan kelas org.lucee.extension.cache.eh.EHCache. Pada Lucee 5.x, EHCache dibundel dalam teras dengan kelas lucee.runtime.cache.eh.EHCache.

Daftar cache dalam Application.cfc, bukan UI Administrator, supaya definisi hidup dalam repo. Contoh ini mendaftar sambungan kedaiCache dan menetapkannya sebagai cache lalai untuk objek dan query:

component {

    this.name = "kedai";

    this.cache.connections[ "kedaiCache" ] = {
        class: "org.lucee.extension.cache.eh.EHCache",
        bundleName: "ehcache.extension",
        storage: false,
        custom: {
            eternal: "false",
            timeToIdleSeconds: "3600",
            timeToLiveSeconds: "7200",
            maxelementsinmemory: "5000",
            memoryevictionpolicy: "LRU",
            overflowtodisk: "true",
            diskpersistent: "false",
            maxelementsondisk: "20000",
            distributed: "off"
        }
    };

    // cachePut dan cacheGet memakai cache ini secara lalai
    this.cache.object = "kedaiCache";
    // cachedwithin pada query memakai cache ini
    this.cache.query = "kedaiCache";

}

Dua baris terakhir ialah kuncinya: this.cache.object menentukan cache untuk cachePut dan cacheGet, manakala this.cache.query menentukan cache untuk cachedwithin. Anda boleh halakan kedua-duanya ke sambungan sama, atau pisahkan kepada dua sambungan dengan saiz berbeza.

Tetapan EHCache yang benar-benar wujud

Kunci dalam struktur custom bukan rekaan, ia dibaca terus oleh sambungan EHCache, dan hurufnya penting. timeToIdleSeconds dan timeToLiveSeconds ditulis camelCase, selebihnya huruf kecil. Lima tetapan yang paling menentukan:

  • eternal ialah "true" atau "false". Apabila "true", entri tidak luput dan dua tetapan masa di bawah diabaikan. Hampir selalu anda mahu "false".
  • timeToIdleSeconds ialah saat entri boleh terbiar tanpa dibaca sebelum dibuang.
  • timeToLiveSeconds ialah jangka hayat maksimum entri dari saat dicipta. Ini tetapan utama untuk mengawal data basi.
  • maxelementsinmemory ialah bilangan maksimum elemen dalam RAM sebelum eviction, dibuang mengikut memoryevictionpolicy seperti "LRU".
  • diskpersistent menentukan sama ada cache ditulis ke cakera supaya kekal selepas JVM dimulakan semula, berbeza daripada overflowtodisk yang hanya melimpahkan elemen ke cakera.

Nilai dalam custom sentiasa rentetan. Menulis eternal: false dan bukannya eternal: "false" boleh membuat sambungan gagal dibina.

memcached sebagai pilihan berasingan

memcached ialah pelayan cache dalam memori yang berjalan sebagai proses berasingan, didaftarkan melalui kelas org.lucee.extension.cache.mc.MemcachedCache, dan data hidup di luar JVM.

Perbezaan itu menentukan bila memcached lebih baik. EHCache menyimpan data dalam RAM proses Java yang sama, jadi beberapa tika Lucee di belakang pengimbang beban mempunyai cache sendiri yang hanyut. memcached memberi satu ruang bersama, jadi semua pelayan melihat nilai baharu serta-merta.

memcached juga melegakan JVM kerana data di luar JVM tidak menyumbang kepada heap; lihat panduan menala JVM Lucee. Tetapi setiap cacheGet melintasi rangkaian, jadi lebih perlahan, dan nilai mesti boleh disiri. Peraturan praktikal: satu pelayan guna EHCache atau RAM; banyak pelayan guna memcached. Untuk sesi yang dikongsi, lihat panduan cache sesi Redis.

Corak cache yang selamat

Cache yang salah reka lebih bahaya daripada tiada cache, kerana ia memulangkan data basi dengan yakin. Empat corak berikut menghalang kebanyakan masalah.

Pertama, kunci cache mesti stabil dan unik. Guna format jenis:id seperti produk:42, dan pastikan setiap nilai berbeza mendapat kunci berbeza.

Kedua, sentiasa beri TTL. Entri tanpa tarikh luput hidup sehingga memori penuh, dan datanya boleh jadi jauh lebih lama daripada disangka.

Ketiga, fahami cache stampede. Apabila entri luput pada trafik puncak, beratus permintaan serentak melihat cacheGet null dan menjalankan kerja mahal yang sama. Kunci berganda menghalangnya:

<cfscript>
key = "statistik:jualan";
nilai = cacheGet( key );
if ( isNull( nilai ) ) {
    lock name="kunci-jana-statistik" timeout="10" throwontimeout="true" {
        // semak sekali lagi di dalam kunci, entri mungkin sudah diisi
        nilai = cacheGet( key );
        if ( isNull( nilai ) ) {
            nilai = kiraStatistikJualan();
            cachePut( key, nilai, createTimeSpan( 0, 0, 5, 0 ) );
        }
    }
}
</cfscript>

Keempat, buang entri yang terjejas apabila data berubah, jangan kosongkan seluruh cache. Corak baca cache-aside:

<cfscript>
key = "produk:senarai";
produk = cacheGet( key );
if ( isNull( produk ) ) {
    produk = queryExecute(
        "SELECT id, nama, harga FROM produk WHERE aktif = :aktif ORDER BY nama",
        { aktif: { value: true, cfsqltype: "cf_sql_bit" } },
        { datasource: "kedai" }
    );
    cachePut( key, produk, createTimeSpan( 0, 0, 30, 0 ) );
}
writeOutput( produk.recordCount );
</cfscript>

Apabila baris itu berubah, buang kunci yang terjejas sahaja:

<cfscript>
// Blok ini berjalan dalam pengendali borang, jadi nilai datang daripada skop form.
// Datasource "kedai" ialah contoh.
queryExecute(
    "UPDATE produk SET nama = :nama, harga = :harga WHERE id = :id",
    {
        nama:  { value: form.nama,  cfsqltype: "cf_sql_varchar" },
        harga: { value: form.harga, cfsqltype: "cf_sql_numeric" },
        id:    { value: form.id,    cfsqltype: "cf_sql_integer" }
    },
    { datasource: "kedai" }
);
cacheRemove( "produk:senarai" );
cacheRemove( "produk:" & form.id );
</cfscript>

Query di atas mengikat nilai melalui cfsqltype dan bukannya menyisip terus ke SQL, supaya input pengguna tidak menjadi suntikan; lihat panduan cfquery dan cfqueryparam.

Untuk query yang dibaca banyak, biarkan Lucee mengurus cache dengan cachedwithin, bukan bendera cache=true:

<cfscript>
// Datasource "kedai" dan jadual produk ialah contoh. Ganti dengan datasource aplikasi anda.
produk = queryExecute(
    "SELECT id, nama FROM produk ORDER BY nama",
    {},
    {
        datasource: "kedai",
        cachedwithin: createTimeSpan( 0, 0, 30, 0 )
    }
);
</cfscript>

cachedwithin menghash SQL dan parameternya sebagai kunci, jadi dua query sama berkongsi satu entri. Apabila data berubah, buang dengan cachedWithinFlush atau jalankan semula dengan cachedwithin sifar.

Mengukur sama ada cache benar-benar membantu

Cache yang tidak diukur ialah tekaan. Dua metrik untuk dijejak ialah masa respons dan hit ratio.

Masa respons diukur dengan getTickCount sebelum dan selepas kerja yang mahal:

<cfscript>
// Kerja yang mahal, diwakili di sini dengan gelung supaya blok ini boleh dijalankan sendiri.
// Dalam aplikasi sebenar, gantikan dengan panggilan cache-aside seperti di atas.
ambilSenaraiProduk = function() {
    var hasil = [];
    for (var i = 1; i <= 20000; i++) {
        arrayAppend(hasil, i * 2);
    }
    return hasil;
};

mula = getTickCount();
produk = ambilSenaraiProduk();
ms = getTickCount() - mula;
writeOutput("bilangan=" & arrayLen(produk) & " ms=" & ms);
</cfscript>

Jika laluan tanpa cache mengambil 80 milisaat dan dengan cache 4 milisaat, cache itu berbaloi. Jika hampir sama, kerja itu mungkin tidak mahal pada mulanya.

Hit ratio ialah nisbah hit berbanding jumlah permintaan. Lucee memaparkan statistik hit dan miss dalam UI Administrator. Ratio rendah bermakna kunci terlalu pelbagai atau TTL terlalu pendek; ratio tinggi tetapi data basi bermakna TTL terlalu panjang atau invalidasi tidak dijalankan. Kalau cacheCount sentiasa menghampiri maxelementsinmemory, cache hampir penuh dan membuang entri berterusan. Log nombor ini seperti dalam panduan log dan pemantauan CFML.

Jadual perbandingan EHCache, memcached dan cache sesi

Ketiga-tiganya kelihatan serupa tetapi melayan tujuan berbeza. EHCache dan memcached ialah enjin cache data, manakala cache sesi ialah cara Lucee menyimpan skop session, dan ia boleh disandarkan kepada salah satu daripada kedua-duanya.

CiriEHCachememcachedCache sesi
Lokasi dataDalam JVM, RAM dengan limpahan cakeraProses luar, di luar JVMBergantung pada storan yang dipilih
Dikongsi antara pelayanTidak secara lalaiYa, satu ruang bersamaYa jika disandarkan pada storan bersama
KependamanPaling rendah, tiada rangkaianLebih tinggi, ada lompatan rangkaianBergantung storan
Tujuan utamaCache objek dan query satu pelayanCache objek merentas banyak pelayanMenyimpan skop session
Bertahan restart JVMYa jika diskpersistent "true"Ya, proses berasinganBergantung storan

Soalan Lazim (FAQ)

Adakah EHCache masih ada dalam Lucee 7?

Ada, tetapi sebagai sambungan org.lucee:ehcache-extension dan bukan sebahagian teras seperti dalam Lucee 5. Kelasnya ialah org.lucee.extension.cache.eh.EHCache.

Bagaimana saya tahu cache memulangkan data basi?

Pengguna melihat data lama selepas mengemaskini rekod, biasanya kerana TTL terlalu panjang atau cacheRemove tidak dipanggil selepas penulisan.

Bolehkah saya guna EHCache dan memcached serentak?

Boleh. EHCache untuk cache objek tempatan, memcached untuk cache yang dikongsi antara pelayan. Guna cacheName pada cachePut untuk menghalakan entri tertentu ke cache lain.

Apa beza cachedwithin dengan cachePut?

cachedwithin membiarkan Lucee mengurus kunci dan penyimpanan hasil query berdasarkan hash SQL. cachePut memberi kawalan penuh terhadap kunci, nilai dan TTL.

Mengapa entri cache hilang lebih awal daripada TTL?

Kemungkinan besar anda mencapai maxelementsinmemory dan polisi eviction seperti LRU membuang entri yang kurang digunakan. Naikkan had memori, pendekkan TTL, atau semak timeToIdleSeconds.

Sumber

Senarai semak

  • Tetapkan this.cache.object dan this.cache.query mengikut lapisan yang anda cache.
  • Daftar EHCache dengan kelas org.lucee.extension.cache.eh.EHCache dan bundleName yang betul untuk Lucee 7.
  • Kekalkan nilai custom sebagai rentetan, dan tulis huruf kunci dengan tepat, terutamanya timeToIdleSeconds lawan maxelementsinmemory.
  • Beri TTL munasabah melalui timeToLiveSeconds, dan eternal sebagai "false" kecuali ada sebab kukuh.
  • Pilih memcached hanya apabila cache perlu dikongsi antara pelayan atau heap JVM perlu dilegakan.
  • Guna kunci jenis:id yang stabil, dan lindungi penjanaan semula daripada stampede dengan kunci berganda.
  • Buang kunci yang terjejas dengan cacheRemove selepas setiap penulisan.
0 komen

0 komen

Log masuk untuk menjawab atau memberi komen.

Log masuk