7 days of WordPress plugins, themes & templates - for free!* Unlimited asset downloads! Start 7-Day Free Trial
Advertisement
  1. Code
  2. PHP

Tambahkan Caching untuk Layer akses Data

Read Time: 17 mins

Indonesian (Bahasa Indonesia) translation by Dika Budiaji (you can also view the original English article)

Halaman web dinamis itu bagus; Anda dapat menyesuaikan halaman hasil untuk user Anda, menunjukkan aktivitas user lainnya, menawarkan produk yang berbeda untuk user Anda berdasarkan history navigasi, dan sebagainya. Tapi semakin dinamis sebuah situs web adalah, query database lain Anda mungkin perlu untuk dilakukan. Sayangnya, query database ini mengkonsumsi sebagian besar dari waktu running time.

Dalam tutorial ini, saya akan menunjukkan cara untuk meningkatkan kinerja, tanpa menjalankan kueri ekstra yang tidak perlu. Kami akan mengembangkan query caching sistem untuk lapisan data kami dengan biaya kecil pemrograman dan deployment.

1. Data Access Layer

Menambahkan layer caching transparan untuk aplikasi ini sering sulit karena desain internal. Bahasa-bahasa berorientasi objek (seperti PHP 5) jauh lebih mudah, tapi itu masih bisa menjadi rumit dengan desain jelek.

Dalam tutorial ini, kita menetapkan titik awal kita dalam aplikasi yang melakukan semua akses database yang melalui kelas terpusat dari mana semua model data mewarisi metode akses dasar database. Kerangka untuk kelas awal ini terlihat seperti ini:

Mari kita menerapkan langkah demi langkah. Pertama, konstruktor yang akan menggunakan perpustakaan PDO untuk interface dengan database:

Kami menghubungkan ke database menggunakan perpustakaan PDO. Untuk kredensial database saya menggunakan kelas statis bernama "app_AppConfig" yang centralizes informasi konfigurasi aplikasi.

Untuk menyimpan koneksi database, kami menggunakan atribut statis ($DB). Kami menggunakan atribut statis untuk berbagi koneksi sama dengan semua contoh-contoh "model_Model", dan, karena itu, kode koneksi dilindungi dengan jika (kita tidak ingin menghubungkan lebih dari sekali).

Dalam baris terakhir dari konstruktor kami mengatur error exception model untuk PDO. Dalam model ini, untuk setiap kesalahan PDO  yang ditemukan, itu throws exception (kelas PDOException) daripda mengembalikan nilai-nilai error. Ini adalah masalah selera, tapi sisa kode dapat disimpan bersih dengan model istimewa, yang baik untuk tutorial ini.

Menjalankan query bisa menjadi sangat kompleks, tetapi dalam kelas ini, kami telah mengambil pendekatan sederhana dengan metode single doStatement():

Metode ini mengeksekusi query, dan mengembalikan array asosiatif dengan seluruh hasil ditetapkan (jika ada). Catatan bahwa kita menggunakan connection statis (self:: $DB). Catatan, juga, bahwa metode ini protected. Hal ini karena kami tidak ingin pengguna untuk menjalankan query yang sewenang-wenang. Selain kami akan memberikan model konkrit ke iser. Kita akan lihat ini kemudian, tapi sebelum Mari kita menerapkan metode terakhir:

Kelas "model_Model" adalah sebuah kelas yang sangat sederhana tetapi nyaman untuk data layering. Meskipun sederhana (itu dapat ditingkatkan dengan fitur-fitur canggih seperti prepared statement jika Anda inginkan), itu hal-hal dasar untuk kita.

Untuk menyelesaikan bagian konfigurasi aplikasi kami, mari kita menulis kelas statis "app_Config":

Seperti yang dinyatakan sebelumnya, kami akan menyediakan konkrit model untuk mengakses database. Sebagai contoh kecil, kita akan menggunakan skema ini sederhana: table dokumen dan index terbalik untuk mencari apakah dokumen berisi kata tertentu atau tidak:

Dari kelas akses data dasar (model_Model), kami memperoleh sebanyak kelas yang diperlukan oleh desain data dari aplikasi kita. Dalam contoh ini, kita dapat memperoleh cukup jelas mereka dua kelas:

Model mereka berasal adalah dimana kita menambahkan informasi publik. Menggunakan mereka sangat sederhana:

Hasil untuk contoh ini mungkin terlihat mirip bahwa (jelas itu tergantung pada data yang sebenarnya):

Apa yang kita telah ditulis ditampilkan dalam diagram UML kelas berikutnya:

2. Caching skema perencanaan

Ketika keadaan mulai collapse dalam server database Anda, sekarang saatnya untuk mengambil istirahat dan mempertimbangkan mengoptimalkan data layer. Setelah dioptimalkan queri Anda, menambahkan indexes yang tepeat, dll, langkah kedua adalah untuk mencoba untuk menghindari queri yang tidak perlu: Mengapa membuat request yang sama ke database pada setiap request user, jika data ini hampir tidak berubah?

Dengan organisasi kelas terencana dan baik decoupled, kita dapat menambahkan layer tambahan untuk aplikasi kita hampir dengan tanpa biaya pemrograman. Dalam kasus ini, kita akan extend kelas "model_Model" untuk menambahkan transparan caching untuk layer database kami.

Dasar-dasar Caching

Karena kita tahu bahwa kita perlu sistem caching, mari kita fokus pada masalah tertentu dan, setelah disortir keluar, kita akan memasukannya dalam model data kami. Untuk sekarang, kita tidak akan berpikir dalam kerangka SQL query. Sangat mudah untuk abstrak sedikit dan membangun sebuah skema yang cukup umum.

Skema caching sederhana terdiri dari [key, data, mana kunci mengidentifikasi aktual data kami ingin menyimpan. Skema ini bukanlah hal yang baru, pada kenyataannya, itu analog dengan array asosiatif PHP, dan kami menggunakannya sepanjang waktu.

Jadi kita perlu cara untuk menyimpan pair, untuk membacanya, dan untuk menghapusnya. Itu sudah cukup untuk membangun interface kami untuk cache helper:

Interface ini cukup mudah: metode get mendapat value, diberikan kunci yang identitas, menempatkan metode set (atau update) nilai untuk kunci tertentu, dan metode Hapus menghapus itu.

Dengan interface ini dalam pikiran, saatnya untuk menerapkan real cache modul pertama kami. Tapi sebelum melakukan ini, kita akan memilih metode penyimpanan data.

Sistem penyimpanan yang mendasar

Keputusan untuk membangun inteface yang umum (seperti cache_CacheHelper) untuk caching pembantu akan memungkinkan kita untuk menerapkan mereka hampir di atas setiap penyimpanan. Tetapi di atas pada sistem penyimpanan apa? Ada banyak dari mereka kita dapat menggunakan: shared memori, file, memcached server atau bahkan database SQLite.

Seringkali masih dianggap remeh, DBM file sempurna untuk sistem caching, dan kita akan menggunakannya dalam tutorial ini.

DBM file bekerja naif pada (key, data) pasangan, dan melakukannya sangat cepat karena internal organisasi B-tree. Mereka juga melakukan kontrol akses bagi kita: kita tidak perlu khawatir tentang memblokir cache sebelum menulis (seperti yang kita harus lakukan pada sistem penyimpanan lain); DBM melakukannya untuk kita.

DBM file tidak didorong oleh server mahal, mereka melakukan pekerjaan mereka dalam sebuah perpustakaan yang ringan pada klien sisi mengakses lokal ke file sebenarnya yang menyimpan data. Pada kenyataannya mereka benar-benar adalah sebuah keluarga format file, semuanya dengan API dasar yang sama untuk (key, data) akses. Beberapa dari mereka memungkinkan repeated key, lainnya konstan dan jangan biarkan menulis setelah menutup file untuk pertama kalinya (CBD), dll. Anda dapat membaca lebih lanjut tentang bahwa pada http://www.php.net/manual/en/dba.requirements.php

Hampir setiap sistem UNIX menginstal satu jenis atau lebih dari perpustakaan ini (mungkin Berkeley DB atau GNU dbm). Untuk contoh ini, kita akan menggunakan "db4" format (format Sleepycat DB4: http://www.sleepycat.com). Saya telah menemukan bahwa perpustakaan ini sering terinstal, tetapi Anda dapat menggunakan perpustakaan apapun yang Anda inginkan (kecuali cdb, tentu saja: kami ingin menulis pada file). Bahkan Anda bisa memindahkan keputusan ini ke kelas "app_AppConfig" dan beradaptasi untuk setiap proyek yang Anda lakukan.

Dengan PHP, kami memiliki dua alternatif untuk berurusan dengan file DBM: ekstensi "dba" (http://php.net/manual/en/book.dba.php) atau modul "PEAR::DBA" (http://pear.php.net/package/DBA). Kita akan menggunakan ekstensi "dba", yang mungkin Anda sudah memiliki terpasang di sistem Anda.

Tunggu sebentar, kami berurusan dengan SQL dan menghasilkan set!

DBM file bekerja dengan string key dan value, tetapi masalah kita adalah untuk menyimpan set hasil SQL (yang dapat bervariasi dalam struktur cukup banyak). Bagaimana kita bisa mengelola untuk mengubah mereka dari satu dunia yang lain?

Nah, untuk key, hal ini sangat mudah karena sebenarnya SQL query string mengidentifikasi satu set data yang sangat baik. Kita dapat menggunakan ringkasan MD5 dari string permintaan untuk mempersingkat kunci. Untuk nilai-nilai, sangat rumit, tapi di sini adalah sekutu anda serialize() / unserialize() fungsi PHP, yang dapat digunakan untuk mengkonversi dari array string dan sebaliknya.

Kita akan melihat bagaimana semua ini bekerja pada bagian berikutnya.

3. Static Caching

Dalam contoh pertama kami, kami akan berurusan dengan cara termudah untuk melakukan caching: caching untuk nilai-nilai statis. Kami akan menulis kelas yang disebut "cache_DBM" mengimplementasikan antarmuka "cache_CacheHelper", hanya seperti itu:

Kelas ini sangat mudah: sebuah mapping antara interface dan dba fungsi. Dalam konstruktor, file diberikan dibuka,
dan mengembalikan handler yang disimpan dalam objek untuk menggunakannya dalam metode lain.

Contoh sederhana penggunaan:

Di bawah ini, Anda akan menemukan apa yang telah kita lakukan di sini dinyatakan sebagai diagram UML kelas:

Sekarang mari kita menambahkan sistem caching untuk model data kami. Kita bisa mengalami perubahan kelas "model_Model" untuk menambahkan caching untuk masing-masing kelas yang diturunkan. Namun, jika kita telah melakukannya, kita akan kehilangan fleksibilitas untuk menetapkan karakteristik caching hanya untuk model-model tertentu, dan saya pikir ini adalah bagian penting dari pekerjaan kami.

Jadi kita akan menciptakan kelas lain, yang disebut "model_StaticCache", yang akan extend "model_Model" dan akan menambah fungsionalitas caching. Mari kita mulai dengan kerangka:

Dalam konstruktor, kita terlebih dahulu memanggil constructor induk untuk menyambung ke database. Kemudian, kami membuat dan menyimpan, statis, sebuah objek "cache_DBM" (jika tidak diciptakan sebelumnya di tempat lain). Kami menyimpan satu contoh untuk setiap nama kelas yang diturunkan karena kami menggunakan satu DBM file untuk setiap satu dari mereka. Untuk itu, kami menggunakan array statis "$cache".

Untuk menentukan di direktori mana yang kita harus menulis file cache, kita telah menggunakan lagi kelas konfigurasi aplikasi: "app_AppConfig".

Dan sekarang: metode doStatement(). Logika untuk metode ini adalah: konversi SQL statement ke valid key, Cari kunci dalam cache, jika ada kembalikan. Jika tidak ditemukan, jalankan dalam database, menyimpan hasil dan mengembalikannya:

Ada dua hal lagi perlu diperhatikan. Pertama, kami menggunakan MD5 dari query sebagai key. Pada kenyataannya, hal ini tidak diperlukan, karena Perpustakaan DBM mendasari menerima key ukuran yang sewenang-wenang, tetapi tampaknya lebih baik untuk mempersingkat key. Jika Anda menggunakan prepared statement, ingat untuk menggabungkan nilai yang sebenarnya untuk kueri string untuk membuat key!

Setelah "model_StaticCache" dibuat, edit konkrit model untuk penggunaannya sepele, Anda hanya perlu mengubah "extends" klausul dalam deklarasi kelas:

Dan itu semua, keajaiban selesai! "model_Document" akan melakukan hanya satu query untuk setiap dokumen untuk mengambil. Tapi kita bisa melakukannya lebih baik.

4. Caching kadaluarsa

Dalam pendekatan pertama kami, setelah query disimpan dalam cache, tetap berlaku selama-lamanya sampai dua hal terjadi: kami menghapus key nya secara eksplisit, atau kami membatalkan tautan DBM file.

Namun pendekatan ini hanya berlaku untuk model data beberapa aplikasi kami: data statis (seperti pilihan menu dan hal-hal semacam ini). Data normal dalam aplikasi kita cenderung lebih dinamis daripada itu.

Pikirkan tentang tabel yang berisi produk yang kami jual di halaman web kami. Sangat tidak mungkin untuk mengubah setiap menit, tapi ada kesempatan bahwa data ini akan berubah (dengan menambahkan produk baru, mengubah harga jual, dll.). Kita perlu cara untuk implement caching, tetapi memiliki cara untuk bereaksi terhadap perubahan dalam data.

Salah satu pendekatan untuk masalah ini adalah untuk mengatur waktu kedaluwarsa untuk data yang tersimpan dalam cache. Ketika kami menyimpan data baru dalam cache, kita mengatur jendela waktu di mana data ini akan berlaku. Setelah itu, data akan membaca dari database lagi dan disimpan ke cache untuk lain waktu.

Seperti sebelumnya, kita dapat membuat kelas lain berasal dari "model_Model" dengan fungsi ini. Saat ini, kita akan menyebutnya "model_ExpiringCache". Kerangka ini mirip dengan "model_StaticCache":

Dalam kelas ini, kami telah memperkenalkan sebuah atribut baru: $expiration. Satu ini akan menyimpan jendela waktu dikonfigurasi untuk data yang valid. Kami menetapkan nilai ini dalam konstruktor, sisa constructor  sama seperti "model_StaticCache":

Sebagian besar pekerjaan datang dalam doStatement. DBM file tidak memiliki internal cara untuk mengontrol data kadaluarsa, sehingga kami harus menerapkan dengan cara kita sendiri. Kami akan melakukannya dengan menyimpan array, seperti ini:

Array semacam ini adalah apa yang kita serialize, dan menyimpan ke dalam cache. "time," key adalah waktu modifikasi data dalam cache, dan "data" adalah data aktual kita ingin simpan. Pada saat membaca, jika kita menemukan bahwa kunci ada, kita membandingkan waktu pembuatan yang disimpan dengan waktu saat ini dan mengembalikan data jika tidak berakhir.

Jika kunci tidak ada atau kadaluarsa, kita melanjutkan mengeksekusi query dan menyimpan hasilnya baru ditetapkan dalam cache sebelum mengembalikannya.

Sederhana!

Sekarang mari kita mengubah "model_Index" ke model dengan kedaluwarsa cache. Seperti yang terjadi, dengan "model_Documents," kita hanya perlu memodifikasi Deklarasi kelas dan mengubah klausa "extends":

Tentang waktu kadaluarsa... beberapa pertimbangan harus dilakukan. Kami menggunakan waktu kedaluwarsa konstan (= 3.600 detik), demi kesederhanaan, dan karena kita tidak ingin memodifikasi seluruh kode kita. Namun, kita dapat dengan mudah memodifikasi dalam banyak cara untuk memungkinkan kita untuk menggunakan masa kadaluarsa yang berbeda, satu untuk masing-masing model. Setelah itu kita akan melihat bagaimana.

Diagram kelas untuk semua pekerjaan kami adalah sebagai berikut:

5. Kadaluarsa yang berbeda

Dalam setiap proyek, saya yakin Anda akan memiliki waktu kadaluarsa yang berbeda untuk hampir setiap model: dari beberapa menit untuk jam, atau bahkan hari.

Kalau saja kita bisa memiliki waktu kadaluarsa yang berbeda untuk setiap model, itu akan menjadi sempurna... tapi, menunggu! Kita dapat melakukannya dengan mudah!

Pendekatan yang paling langsung adalah dengan menambahkan sebuah argumen ke konstruktor, sehingga konstruktor baru untuk "model_ExpiringCache" akan menjadi satu ini:

Kemudian, jika kita ingin model dengan waktu kadaluarsa 1 hari (1 hari = 24 jam = 1.440 menit = 86,400 detik), kita dapat mencapainya dengan cara ini:

Dan itu semua. Namun, kekurangannya adalah bahwa kita harus memodifikasi semua model data.

Cara lain untuk melakukannya adalah untuk mendelegasikan tugas untuk "app_AppConfig":

Dan kemudian menambahkan panggilan ke metode baru ini pada constructor "model_ExpiringCache", seperti ini:

Metode terbaru ini memungkinkan kita untuk melakukan hal-hal mewah, seperti menggunakan nilai-nilai kadaluarsa yang berbeda untuk lingkungan production atau development dalam cara yang lebih terpusat. Lagi pula, Anda dapat memilih yang di inginkan.

Di UML, proyek total terlihat seperti ini:

6. beberapa keberatan

Ada beberapa query yang tidak dapat di-cache. Yang paling jelas adalah memodifikasi query, seperti INSERT, DELETE atau UPDATE. query ini harus tiba ke database server.

Tetapi bahkan dengan query SELECT, ada beberapa situasi di mana sebuah sistem caching dapat creaet masalah. Lihatlah query seperti ini:

Query ini memilih secara acak 10 banner untuk zona "home" situs web kami. Hal ini dimaksudkan untuk menghasilkan gerakan dalam banner yang ditampilkan di home, tetapi jika kita men-cache query ini, pengguna tidak akan melihat setiap gerakan, sampai data cache berakhir.

Rand() fungsi ini tidak deterministik (karena hal ini tidak now() atau lainnya); Jadi itu akan mengembalikan nilai berbeda pada setiap eksekusi. Jika kita cache, kami akan membekukan hanya salah satu hasil tersebut untuk semua caching periode, dan karena itu melanggar fungsi tersebut.

Tetapi dengan simple refactoring, kita dapat memperoleh manfaat dari cache dan menunjukkan keacakan pseudo:

Apa yang kita lakukan di sini adalah untuk cache lima puluh konfigurasi spanduk acak yang berbeda, dan memilih mereka secara acak. 50 SELECT yang akan terlihat seperti ini:

Kami telah menambahkan kondisi konstan untuk Pilih, yang telah ada biaya untuk database server namun menuliskan 50 kunci yang berbeda untuk sistem caching. Pengguna akan perlu untuk memuat halaman 50 kali untuk melihat semua banner konfigurasi yang berbeda; Jadi efek dinamis dicapai. Biaya adalah lima puluh query ke database untuk mengambil cache.

7. Benchmark

Apa manfaat yang bisa kita harapkan dari sistem caching kami baru?

Pertama, harus dikatakan bahwa, dalam raw performance, kadang-kadang implementasi baru kami akan berjalan lebih lambat dari query database, khusus dengan sangat sederhana, dioptimalkan dengan baik queries. Tapi untuk pertanyaan dengan bergabung, cache DBM kami akan berjalan lebih cepat.

Namun, kami memecahkan masalahnya bukan raw performance. Anda akan pernah cadangan database server untuk tes Anda dalam produksi. Anda mungkin akan memiliki sebuah server dengan beban kerja yang tinggi. Dalam situasi ini, bahkan tercepat query dapat berjalan lambat, tetapi dengan skema kami caching, kita bahkan tidak menggunakan server dan, pada kenyataannya, kami mengurangi beban kerja. Sehingga peningkatan kinerja real akan datang dalam bentuk lebih petisi per kedua disajikan.

Situs web yang saya sedang mengembangkan, saya telah melakukan benchmark sederhana untuk memahami manfaat dari cache. Server sederhana: berjalan Ubuntu 8.10 berjalan di atas sebuah AMD Athlon 64 X2 5600 +, dengan 2 GB RAM dan PATA hard disk lama. Menjalankan Apache dan MySQL 5.0, yang datang dengan distribusi Ubuntu tanpa tuning.

Ujiannya adalah untuk menjalankan Apache benchmark program (ab) dengan 1, 5 dan 10 klien bersamaan loading halaman kali 1.000 dari situs pembangunan. Halaman yang sebenarnya adalah produk detail yang memiliki tidak kurang dari 20 queries: menu isi, detail produk, direkomendasikan produk, spanduk, dll.

Tanpa cache hasilnya 4.35 p/s 1 klien, 8,25 untuk 5 klien, dan 8.29 untuk 10 klien. Dengan cache (kadaluarsa yang berbeda), hasilnya 25.55 p/s dengan 1 klien, 49.01 untuk 5 klien, dan 48.74 untuk 10 klien.

Akhir pikiran

Aku telah menunjukkan Anda cara mudah untuk memasukkan caching ke model data Anda. Tentu saja, ada sejumlah besar alternatif, tetapi ini hanyalah salah satu pilihan yang Anda miliki.

Kami telah menggunakan file DBM lokal untuk menyimpan data, tetapi ada bahkan lebih cepat alternatif yang mungkin Anda pertimbangkan menjelajahi. Beberapa ide untuk masa depan: menggunakan APC apc_store() fungsi sebagai mendasari sistem penyimpanan, berbagi memori untuk data benar-benar penting, menggunakan memcached, dll.

Saya harap Anda telah menikmati tutorial ini sebanyak aku menulis itu. Happy caching!

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Scroll to top
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.