Panduan Pasukan Lucee MY

Cache Query dengan cachedWithin dalam Lucee, Diukur di Rig

cachedWithin menyimpan hasil query dalam cache Lucee. Diukur pada Lucee 7.1: kos hit, kunci cache, nilai basi selepas UPDATE, dan cara membuang entri.

Cache Query dengan cachedWithin dalam Lucee, Diukur di Rig
0
Kandungan

cachedWithin menyimpan hasil satu query di dalam cache Lucee selama tempoh yang anda tetapkan, dan permintaan seterusnya yang menjalankan SQL serta parameter yang sama menerima hasil itu tanpa menyentuh pangkalan data. Diukur pada Lucee 7.1.1.17-RC dengan MySQL 8.4: query yang mengambil 302 ms jatuh ke 1 ms pada panggilan kedua, dan cache hit pada query biasa memakan 0.1 ms.

Artikel ini menjawab soalan selepas cache dipasang: apa yang menjadi kunci, bila data basi, dan cara membuang entri tanpa menunggu tempohnya tamat. Asas cache aplikasi ada dalam caching dan prestasi Lucee.

Apa yang disimpan dan apa yang dibaca balik

Lucee menyimpan objek hasil query di dalam cache query aplikasi, dan pada panggilan berikutnya ia mengira kunci daripada SQL dan tetapan query sebelum mencari hasil tersimpan. Hasil itu membawa bendera cached dan cachedWithinId dalam struktur result, dan bendera itulah bukti paling jujur kerana pada jadual kecil query selesai dalam satu milisaat. Ujian di bawah memakai SLEEP(0.3) supaya kos pangkalan data kelihatan.

<cfscript>
sql = "SELECT SLEEP(0.3) AS tunda, NOW() AS masa";
opts = {datasource = "kedai", cachedwithin = createTimeSpan(0, 0, 5, 0), result = "r"};

mula = getTickCount();
queryExecute(sql, {}, {datasource = "kedai"});
tanpaCache = getTickCount() - mula;

mula = getTickCount();
q1 = queryExecute(sql, {}, opts);
dingin = getTickCount() - mula;
bendera1 = r.cached;

mula = getTickCount();
q2 = queryExecute(sql, {}, opts);
panas = getTickCount() - mula;

writeOutput("tanpa cache " & tanpaCache & " ms | dingin " & dingin & " ms (cached=" & bendera1
    & ") | panas " & panas & " ms (cached=" & r.cached & ") | masa sama=" & (dateCompare(q1.masa, q2.masa) == 0));
</cfscript>
tanpa cache 302 ms | dingin 302 ms (cached=false) | panas 1 ms (cached=true) | masa sama=true
KeadaanMasa panggilan
Tanpa cache302 ms
cachedWithin, cache dingin302 ms
cachedWithin, cache hit1 ms
Cache hit pada query biasa, purata dua puluh panggilan0.1 ms

Panggilan dingin tidak menjadi lebih perlahan dengan ketara, kerana kos tambahannya hanya satu carian dan satu penulisan cache. Faedah sebenar datang daripada bilangan query: halaman dengan lapan query senarai tidak lagi membayar kos itu pada setiap permintaan, hanya pada permintaan pertama dalam tempoh cache.

Cache bukan pengganti index: ia menyembunyikan query perlahan daripada pelawat pertama, bukan memperbaikinya. Apabila cache mengurangkan query, sambungan juga berkurang, jadi tala connection pool Lucee dan profil prestasi CFML perlu dilihat bersama.

Kunci cache: apa yang membezakan satu entri

Kunci ialah hash daripada SQL dan parameternya, nama datasource, nama pengguna dan kata laluan datasource, returntype, dan maxrows. Nama query tidak masuk ke dalam kunci, dan itu boleh diukur: dua query dengan nama berbeza tetapi SQL yang sama berkongsi satu entri.

<cfscript>
sql = "SELECT SLEEP(0.2) AS tunda, ? AS nilai";
opts = {datasource = "kedai", cachedwithin = createTimeSpan(0, 0, 10, 0), result = "r"};

mula = getTickCount(); queryExecute(sql, [1], opts); satu = getTickCount() - mula;
mula = getTickCount(); queryExecute(sql, [2], opts); dua = getTickCount() - mula;
mula = getTickCount(); queryExecute(sql, [1], opts); semula = getTickCount() - mula;

queryExecute("SELECT id FROM produk WHERE id = 1", {}, opts);
asas = r.cached;
queryExecute("SELECT id FROM produk WHERE id = 1   ", {}, opts);
hujung = r.cached;
queryExecute("SELECT  id FROM produk WHERE id = 1", {}, opts);
dalaman = r.cached;

writeOutput("[1]=" & satu & " ms, [2]=" & dua & " ms, [1] semula=" & semula
    & " ms | cached asas=" & asas & ", ruang hujung=" & hujung & ", ruang dalaman=" & dalaman);
</cfscript>
[1]=202 ms, [2]=202 ms, [1] semula=1 ms | cached asas=false, ruang hujung=true, ruang dalaman=false
Perubahan pada queryEntri cache sama?
Parameter berbeza, [1] lawan [2]Tidak, query dijalankan semula
Nama query berbeza, SQL dan parameter samaYa, entri dikongsi
Ruang tambahan di hujung SQLYa, ruang hujung dipangkas sebelum kunci dikira
Satu ruang tambahan di tengah SQLTidak, entri baharu

Dua akibat praktikal: SQL yang dibina secara dinamik dengan ruang atau baris baharu yang berubah-ubah menghasilkan entri berbeza untuk query yang sama, jadi cache tidak pernah panas; dan hasil kosong juga dicache, jadi query sifar baris tidak dijalankan semula sehingga tempohnya tamat.

Butiran: result mengandungi cached, cachedWithinId, executionTime, RECORDCOUNT dan SQL. Nilai cachedWithinId boleh dipakai untuk membuang entri tertentu; bentuk tag dan fungsi query ada dalam queryExecute lawan cfquery.

Bahaya yang paling kerap: nilai lama selepas UPDATE

Query ditanda cache, data diubah, dan halaman masih memaparkan nilai lama kerana kunci yang sama masih ada.

<cfscript>
sql = "SELECT harga FROM produk WHERE id = 1";
opts = {datasource = "kedai", cachedwithin = createTimeSpan(0, 0, 10, 0)};
queryExecute("UPDATE produk SET harga = 12.50 WHERE id = 1", {}, {datasource = "kedai"});

q1 = queryExecute(sql, {}, opts);
queryExecute("UPDATE produk SET harga = 99.00 WHERE id = 1", {}, {datasource = "kedai"});
q2 = queryExecute(sql, {}, opts);
q3 = queryExecute(sql, {}, {datasource = "kedai"});

writeOutput("sebelum=" & q1.harga & ", dari cache=" & q2.harga & ", dari pangkalan data=" & q3.harga);
queryExecute("UPDATE produk SET harga = 12.50 WHERE id = 1", {}, {datasource = "kedai"});
</cfscript>
sebelum=12.5, dari cache=12.5, dari pangkalan data=99

cachedWithin tidak tahu apa-apa tentang UPDATE. Ia menyimpan hasil, bukan data, dan tiada apa dalam enjin membatalkan entri apabila jadual berubah. Jadi cache hanya bacaan yang boleh basi tanpa merosakkan keputusan: senarai kategori, senarai tag, tetapan laman dan senarai wilayah sesuai, manakala baki akaun, harga dan stok tidak sesuai untuk cache panjang melainkan entrinya dibuang selepas setiap tulisan.

Membuang entri yang sudah basi

Cara paling mudah memerlukan satu baris dalam Application.cfc: cache query lalai.

// dalam Application.cfc
this.cache.connections["queryCache"] = {
    class: "lucee.runtime.cache.ram.RamCache",
    storage: false,
    custom: { timeToIdleSeconds: 3600, timeToLiveSeconds: 3600 },
    default: "query"
};

Tanpa cache lalai itu, caching asas masih berjalan tetapi fungsi pembuangannya gagal dengan mesej there is no default query cache defined, you need to define this default cache in the Lucee Administrator. Diukur: cachedWithin masih memulangkan hasil cache dalam satu milisaat, dan pembuangan entri hanya berfungsi selepas cache lalai ditambah.

<cfscript>
namaCache = cacheGetDefaultCacheName("query");
sql = "SELECT SLEEP(0.2) AS tunda, NOW() AS masa";
opts = {datasource = "kedai", cachedwithin = createTimeSpan(0, 0, 30, 0), tags = "artikel,artikel:senarai"};
queryExecute(sql, {}, opts);
q = queryExecute(sql, {}, opts);

dibuang = cachedWithinFlush(q);
mula = getTickCount(); queryExecute(sql, {}, opts); selepasFlush = getTickCount() - mula;

queryExecute(sql, {}, opts);
dibuangTag = cacheClear(["artikel:senarai"], namaCache);
mula = getTickCount(); queryExecute(sql, {}, opts); selepasTag = getTickCount() - mula;

systemCacheClear("query");
mula = getTickCount(); queryExecute(sql, {}, opts); selepasSemua = getTickCount() - mula;

writeOutput("flush=" & dibuang & " (" & selepasFlush & " ms) | tag=" & dibuangTag
    & " (" & selepasTag & " ms) | systemCacheClear (" & selepasSemua & " ms)");
</cfscript>
flush=true (202 ms) | tag=1 (202 ms) | systemCacheClear (202 ms)

cachedWithinFlush() dan cachedWithinId() diperkenalkan dalam Lucee 7.1 dan bekerja pada query, fungsi dan hasil cfhttp. cacheClear() mengikut tag mesti diberi nama cache query. systemCacheClear("query") membuang seluruh cache query tetapi juga entri yang masih sah.

Dua cara lain tidak memerlukan cache lalai. Jalankan query yang sama dengan tempoh sifar, cachedWithin="#createTimeSpan(0,0,0,0)#": entri lama dibuang dan hasilnya tidak disimpan. Atau guna cachePut() dengan kunci sendiri, seperti di bawah.

Entri cache query juga tidak boleh disenaraikan: cacheGetAllIds() memulangkan sifar kunci walaupun entri baru ditulis, jadi pemantauan mesti bergantung pada cachedWithinId daripada result.

Bila jangan guna cachedWithin

Kunci cache tidak mengandungi identiti pelawat. Ia mengandungi SQL, parameter, datasource, pengguna dan kata laluan datasource, returntype dan maxrows. Jadi query yang hasilnya bergantung pada siapa yang bertanya akan memberi hasil orang lain kepada semua orang, kecuali identiti itu masuk ke dalam SQL atau parameter. Kalau senarai pesanan berbeza bagi setiap ahli, masukkan id ahli ke dalam WHERE atau jangan cache.

Tiga keadaan lain: query selepas INSERT atau UPDATE dalam aliran yang sama akan memulangkan keadaan sebelum tulisan; query murah dan jarang tidak berbaloi dengan risiko basi; dan hasil yang sangat besar memegang seluruh objek query dalam memori, manakala cache RAM penuh menolak entri lain keluar.

Corak kunci sendiri untuk kumpulan query

Untuk membuang sekumpulan query mengikut kumpulan, cachedWithin tidak sesuai kerana kuncinya hash yang anda tidak boleh pilih. Guna cachePut() dan cacheGet() dengan awalan kumpulan pada kunci, kemudian cacheClear() dengan corak.

<cfscript>
function senaraiProduk(required string kumpulan, required string sql) {
    var kunci = lCase(kumpulan) & hash(sql, "MD5");
    var q = cacheGet(id = kunci, cacheName = "myCache");
    if (isNull(q)) {
        q = queryExecute(sql, {}, {datasource = "kedai"});
        cachePut(id = kunci, value = q, timeSpan = createTimeSpan(0, 0, 30, 0), cacheName = "myCache");
        return { q: q, sumber: "pangkalan data" };
    }
    return { q: q, sumber: "cache" };
}

sql = "SELECT id, nama FROM produk ORDER BY id LIMIT 3";
a = senaraiProduk("produk:", sql);
b = senaraiProduk("produk:", sql);
dibuang = cacheClear("produk:*", "myCache");
c = senaraiProduk("produk:", sql);

writeOutput("pertama=" & a.sumber & ", kedua=" & b.sumber & ", cacheClear="
    & dibuang & ", selepas clear=" & c.sumber);
</cfscript>
pertama=pangkalan data, kedua=cache, cacheClear=1, selepas clear=pangkalan data

cacheName mesti menunjuk cache yang wujud dalam this.cache.connections aplikasi anda; dalam rig ujian namanya myCache. Corak ini memberi kunci yang boleh dibaca dan pembuangan mengikut kumpulan, dengan harga logik simpan dan ambil yang anda tulis sendiri.

Soalan Lazim

Adakah cachedWithin memerlukan cache yang dikonfigurasi dalam Lucee Admin? Tidak untuk caching asas. Diukur pada Lucee 7.1.1.17-RC: cacheGetDefaultCacheName("query") membaling ralat, tetapi query dengan cachedWithin masih memulangkan hasil cache dalam satu milisaat. Yang memerlukan cache lalai ialah cachedWithinFlush() dan cacheClear() mengikut tag.

Berapa lama data boleh basi? Selama tempoh yang anda tetapkan, dan tiada apa membatalkannya lebih awal kecuali anda membuangnya. Query bertanda sepuluh minit boleh memulangkan nilai sebelum UPDATE selama sepuluh minit.

Adakah nama query sebahagian daripada kunci cache? Tidak. Dua cfquery dengan nama berbeza tetapi SQL dan parameter yang sama berkongsi satu entri. Sebahagian dokumen menyebut nama query sebagai syarat padanan; ujian di rig menunjukkan sebaliknya.

Bolehkah saya memilih kunci cache sendiri? Tidak dengan cachedWithin. Atribut cacheId tidak dilaksanakan dalam Lucee dan cacheName sudah tidak berfungsi. Untuk kunci sendiri, guna cachePut() dan cacheGet().

Kenapa query yang sama dijalankan semula walaupun cache sepatutnya panas? Biasanya kerana SQL yang dihantar berbeza sedikit: ruang tambahan di tengah, susunan lajur berubah, atau jenis parameter berbeza. Bandingkan rentetan SQL sebenar, bukan kod yang membinanya.

Sumber

0 komen

0 komen

Log masuk untuk menjawab atau memberi komen.

Log masuk