Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. PHP
Code

Tambah Caching ke Lapisan Akses Data

by
Difficulty:IntermediateLength:LongLanguages:

Malay (Melayu) translation by Dwi Bagus Nurrohman (you can also view the original English article)

Laman web dinamik adalah hebat; anda boleh menyesuaikan halaman yang dihasilkan kepada pengguna anda, menunjukkan aktiviti pengguna lain, menawarkan produk yang berbeza kepada pelanggan anda berdasarkan sejarah navigasi mereka, dan sebagainya. Tetapi laman web yang lebih dinamik adalah, lebih banyak pertanyaan pangkalan data yang mungkin perlu anda lakukan. Malangnya, pertanyaan pangkalan data ini mengambil sebahagian besar masa berjalan anda.

Dalam tutorial ini, saya akan menunjukkan cara untuk meningkatkan prestasi, tanpa memerlukan pertanyaan tambahan yang tidak perlu. Kami akan membangunkan sistem caching pertanyaan untuk lapisan data kami dengan pengaturcaraan kecil dan kos penyebaran.

1. Lapisan Akses Data

Menambah lapisan caching secara telus kepada aplikasi sering sukar kerana reka bentuk dalaman. Dengan bahasa berorientasikan objek (seperti PHP 5), ia lebih mudah, tetapi ia masih boleh rumit oleh reka bentuk yang kurang baik.

Dalam tutorial ini, kami menetapkan titik permulaan dalam aplikasi yang melakukan semua akses pangkalan data melalui kelas berpusat yang mana semua model data mewarisi kaedah akses pangkalan data asas. Tali untuk kelas permulaan ini kelihatan seperti ini:

Mari laksanakan langkah demi langkah. Pertama, pembina yang akan menggunakan perpustakaan PDO untuk menyambung dengan pangkalan data:

Kami menyambung ke pangkalan data menggunakan perpustakaan PDO. Untuk kelayakan pangkalan data saya menggunakan kelas statik bernama "app_AppConfig" yang memusatkan maklumat konfigurasi aplikasi.

Untuk menyimpan sambungan pangkalan data, kami menggunakan atribut statik ($ DB). Kami menggunakan atribut statik untuk berkongsi sambungan yang sama dengan semua contoh "model_Model", dan, oleh kerana itu, kod sambungan dilindungi jika (kami tidak mahu menyambung lebih daripada sekali).

Dalam barisan terakhir pembina, kami menetapkan model ralat pengecualian untuk PDO. Dalam model ini, untuk setiap kesilapan yang diperoleh PDO, ia melemparkan pengecualian (kelas PDOException) dan bukannya nilai ralat kembali. Ini adalah satu perkara rasa, tetapi seluruh kod tersebut dapat dijaga dengan bersih dengan model yang luar biasa, yang bagus untuk tutorial ini.

Melaksanakan pertanyaan boleh menjadi sangat rumit, tetapi dalam kelas ini kita telah mengambil pendekatan yang mudah dengan satu kaedah doStatement ():

Kaedah ini melaksanakan pertanyaan, dan mengembalikan array bersekutu dengan keseluruhan hasil set (jika ada). Perhatikan bahawa kami menggunakan sambungan statik (sendiri :: $ DB). Perhatikan, juga, bahawa kaedah ini dilindungi. Ini kerana kami tidak mahu pengguna melaksanakan pertanyaan sewenang-wenangnya. Daripada itu, kami akan menyediakan model konkrit kepada pengguna. Kita akan lihat ini kemudian, tetapi sebelum kita melaksanakan kaedah terakhir:

Kelas "model_Model" adalah kelas yang sangat mudah tetapi mudah untuk lapisan data. Walaupun ia mudah (ia boleh dipertingkatkan dengan ciri-ciri canggih seperti penyataan yang disediakan jika anda mahu), ia melakukan perkara asas untuk kami.

Untuk melengkapkan bahagian konfigurasi aplikasi kami, mari kita tulis kelas statik "app_Config":

Seperti yang dinyatakan sebelum ini, kami akan menyediakan model konkrit untuk mengakses pangkalan data. Sebagai contoh kecil, kita akan menggunakan skema mudah ini: meja dokumen dan indeks terbalik untuk mencari sama ada dokumen mengandungi perkataan yang diberikan atau tidak:

Dari kelas akses data asas (model_Model), kami memperoleh banyak kelas seperti yang diperlukan oleh reka bentuk data permohonan kami. Dalam contoh ini, kita boleh memperoleh dua kelas yang jelas:

Model-model yang diperolehi adalah di mana kita menambah maklumat awam. Menggunakannya sangat mudah:

Hasilnya untuk contoh ini mungkin kelihatan serupa dengan itu (jelasnya bergantung pada data sebenarnya anda):

Apa yang telah kita tulis ditunjukkan dalam rajah kelas UML yang seterusnya:

2. Merancang Skim Caching kami

Apabila sesuatu mula runtuh di pelayan pangkalan data anda, sudah tiba masanya untuk berehat dan mempertimbangkan mengoptimumkan lapisan data. Selepas mengoptimumkan pertanyaan anda, menambah indeks yang sesuai, dan lain-lain, langkah kedua adalah untuk mengelakkan pertanyaan yang tidak perlu: mengapa permintaan yang sama kepada pangkalan data pada setiap permintaan pengguna, jika data ini tidak berubah?

Dengan organisasi kelas yang dirancang dengan baik dan baik, kami boleh menambah lapisan tambahan untuk aplikasi kami hampir tanpa kos pengaturcaraan. Dalam kes ini, kami akan melanjutkan kelas "model_Model" untuk menambah caching telus ke lapisan pangkalan data kami.

Asas Caching

Oleh kerana kita tahu bahawa kita memerlukan sistem caching, mari kita memberi tumpuan kepada masalah tersebut dan, setelah disusun, kita akan mengintegrasikannya dalam model data kami. Buat masa ini, kami tidak akan berfikir dari segi pertanyaan SQL. Ia mudah untuk abstrak sedikit dan membina skim yang cukup umum.

Skema caching yang paling mudah terdiri daripada pasangan [kunci, data], di mana kunci mengenal pasti data sebenar yang ingin kami simpan. Skema ini tidak baru, sebenarnya, ia sama dengan susunan asosiasi PHP, dan kami menggunakannya sepanjang masa.

Oleh itu, kita memerlukan satu cara untuk menyimpan pasangan, membacanya, dan memadamkannya. Itu cukup untuk membina antara muka kami untuk pembantu cache:

Antara muka adalah agak mudah: kaedah mendapatkan mendapat nilai, memandangkan kekunci pengenalpastian, kaedah meletakkan set (atau kemas kini) nilai untuk kunci yang diberikan, dan kaedah padam memadamnya.

Dengan antaramuka ini, sudah tiba masanya untuk melaksanakan modul cache pertama kami. Tetapi sebelum melakukannya, kami akan memilih kaedah penyimpanan data.

Sistem Penyimpanan yang Berdasar

Keputusan untuk membina antara muka yang sama (seperti cache_CacheHelper) untuk pembantu caching akan membolehkan kami melaksanakannya hampir di atas setiap simpanan. Tetapi di atas sistem penyimpanan apa? Terdapat banyak perkara yang boleh kita gunakan: memori bersama, fail, pelayan memcached atau pangkalan data SQLite.

Selalunya dipandang ringan, fail DBM sesuai untuk sistem caching kami, dan kami akan menggunakannya dalam tutorial ini.

Fail-fail DBM berfungsi dengan naif pada pasangan (kunci, data), dan melakukannya dengan cepat kerana organisasi B-pokok dalamannya. Mereka juga melakukan kawalan akses untuk kita: kita tidak perlu risau tentang menyekat cache sebelum menulis (seperti yang kita perlu lakukan pada sistem storan lain); DBM melakukannya untuk kita.

Fail DBM tidak didorong oleh pelayan mahal, mereka melakukan kerja mereka di dalam perpustakaan yang ringan di sisi pelanggan yang mengakses secara tempatan ke fail sebenar yang menyimpan data. Malah mereka sebenarnya adalah keluarga format fail, semuanya dengan API asas yang sama untuk akses (kunci, data). Sesetengah daripada mereka membenarkan kekunci berulang, yang lain adalah malar dan tidak membenarkan menulis selepas menutup fail buat kali pertama (cdb), dan lain-lain. Anda boleh membaca lebih lanjut mengenai itu di http://www.php.net/manual/en /dba.requirements.php

Hampir setiap sistem UNIX memasang satu jenis atau lebih daripada perpustakaan-perpustakaan ini (mungkin Berkeley DB atau GNU dbm). Untuk contoh ini, kami akan menggunakan format "db4" (format Sleepycat DB4: http://www.sleepycat.com). Saya telah mendapati bahawa perpustakaan ini sering dipasangkan, tetapi anda boleh menggunakan mana-mana perpustakaan yang anda mahukan (kecuali cdb, tentu saja: kami mahu menulis pada fail itu). Malah anda boleh memindahkan keputusan ini ke kelas "app_AppConfig" dan menyesuaikannya untuk setiap projek yang anda lakukan.

Dengan PHP, kami mempunyai dua alternatif untuk menangani fail DBM: sambungan "dba" (http://php.net/manual/en/book.dba.php) atau modul "PEAR :: DBA" (http://pear.php.net/package/DBA). Kami akan menggunakan pelanjutan "dba", yang mungkin sudah dipasang di dalam sistem anda.

Tunggu seminit, kita berurusan dengan SQL dan set hasil!

Fail DBM berfungsi dengan rentetan untuk kunci dan nilai, tetapi masalah kami adalah untuk menyimpan set hasil SQL (yang boleh berbeza dalam struktur yang cukup banyak). Bagaimanakah kita dapat menukar mereka dari satu dunia ke yang lain?

Nah, untuk kekunci, sangat mudah kerana string pertanyaan SQL sebenarnya mengenal pasti satu set data dengan sangat baik. Kita boleh menggunakan MD5 mencernakan rentetan pertanyaan untuk memendekkan kunci. Untuk nilai-nilai, ia lebih rumit, tetapi di sini sekutu anda adalah fungsi bersaiz bersiri () / unserialize () PHP, yang boleh digunakan untuk menukar dari array ke rentetan dan sebaliknya.

Kami akan melihat bagaimana semua ini berfungsi di bahagian seterusnya.

3. Statik Caching

Dalam contoh pertama kami, kami akan berurusan dengan cara yang paling mudah untuk melakukan caching: caching untuk nilai statik. Kami akan menulis kelas yang dipanggil "cache_DBM" yang melaksanakan antara muka "cache_CacheHelper", seperti itu:

Kelas ini sangat mudah: pemetaan antara antara muka dan fungsi dba kami. Dalam pembina, fail yang diberikan dibuka,
dan pengendali yang dikembalikan disimpan dalam objek untuk menggunakannya dalam kaedah lain.

Contoh penggunaan yang mudah:

Di bawah ini, anda akan mendapati apa yang telah kami lakukan di sini dinyatakan sebagai rajah kelas UML:

Sekarang mari tambahkan sistem caching ke model data kami. Kita boleh mengubah kelas "model_Model" untuk menambah caching ke setiap kelas yang diturunkannya. Tetapi, jika kami melakukannya, kami akan kehilangan kelenturan untuk menetapkan ciri caching hanya kepada model tertentu, dan saya fikir ini adalah bahagian penting dalam tugas kami.

Jadi kami akan membuat kelas lain, yang dipanggil "model_StaticCache", yang akan memanjangkan "model_Model" dan akan menambah fungsi caching. Mari kita mulakan dengan kerangka:

Dalam pembina, pertama kami menghubungi pembina induk untuk menyambung ke pangkalan data. Kemudian, kami membuat dan menyimpan, secara statistik, objek "cache_DBM" (jika tidak dibuat sebelum tempat lain). Kami menyimpan satu contoh untuk setiap nama kelas yang diturunkan kerana kami menggunakan satu fail DBM untuk setiap satu daripada mereka. Untuk tujuan itu, kami menggunakan "$cache" array statik.

Untuk menentukan di direktori mana kita perlu menulis fail cache, kami telah menggunakan semula kelas konfigurasi aplikasi: "app_AppConfig".

Dan sekarang: kaedah doStatement(). Logik untuk kaedah ini ialah: menukar pernyataan SQL ke kekunci yang sah, cari kunci dalam cache, jika didapati mengembalikan nilai. Sekiranya tidak dijumpai, laksanakannya dalam pangkalan data, simpan hasilnya dan kembalikannya:

Terdapat dua perkara lagi yang perlu diperhatikan. Pertama, kami menggunakan MD5 pertanyaan sebagai kunci. Malah, tidak perlu, kerana perpustakaan DBM yang mendasar menerima kunci saiz sewenang-wenangnya, tetapi nampaknya lebih baik untuk memendekkan kunci. Sekiranya anda menggunakan penyataan yang disediakan, ingatlah untuk menggabungkan nilai sebenar pada rentetan pertanyaan untuk membuat kunci!

Setelah "model_StaticCache" dicipta, mengubah model konkrit untuk penggunaannya adalah sepele, anda hanya perlu mengubah fasal "extends "nya dalam perisytiharan kelas:

Dan itu sahaja, keajaiban itu dilakukan! "model_Document" akan melaksanakan hanya satu pertanyaan untuk setiap dokumen untuk diambil. Tetapi kita boleh melakukannya dengan lebih baik.

4. Tempoh Caching

Dalam pendekatan pertama kami, sekali pertanyaan disimpan dalam cache, ia tetap berlaku selama-lamanya sehingga dua perkara berlaku: kami memadamkan kuncinya secara eksplisit, atau kami menyahpautkan fail DBM.

Walau bagaimanapun, pendekatan ini hanya sah untuk beberapa model data permohonan kami: data statik (seperti pilihan menu dan perkara semacam ini). Data biasa dalam aplikasi kami mungkin lebih dinamik daripada itu.

Fikirkan jadual yang mengandungi produk yang kami jual di laman web kami. Ia tidak mungkin berubah setiap minit, tetapi ada peluang bahawa data ini akan berubah (dengan menambah produk baru, menukar harga jual, dll.). Kami memerlukan satu cara untuk melaksanakan caching, tetapi mempunyai cara untuk bertindak balas terhadap perubahan dalam data.

Satu pendekatan untuk masalah ini adalah untuk menetapkan masa tamat tempoh kepada data yang disimpan dalam cache. Apabila kami menyimpan data baru dalam cache, kami menetapkan tetingkap masa di mana data ini akan sah. Selepas itu, data akan dibaca dari pangkalan data sekali lagi dan disimpan ke dalam cache untuk tempoh masa yang lain.

Seperti dahulu, kita boleh membuat satu lagi kelas yang diturunkan daripada "model_Model" dengan fungsi ini. Kali ini, kami akan memanggilnya "model_ExpiringCache". Kerangka ini sama dengan "model_StaticCache":

Di kelas ini, kami telah memperkenalkan atribut baru: $ expiration. Yang ini akan menyimpan tetingkap masa yang dikonfigurasikan untuk data yang sah. Kami menetapkan nilai ini dalam pembina, seluruh pembina adalah sama seperti dalam "model_StaticCache":

Sebahagian besar pekerjaan datang dalam doStatement. Fail DBM tidak mempunyai cara dalaman untuk mengawal tamat tempoh data, jadi kita mesti melaksanakan sendiri. Kami akan melakukannya dengan menyimpan array, seperti ini:

Arus semacam ini adalah apa yang kami siri, dan simpan ke dalam cache. Kunci "key" ialah masa pengubahsuaian data dalam cache, dan "data" adalah data sebenar yang ingin kami simpan. Pada waktu baca, jika kita mendapati bahawa kekunci itu wujud, kita membandingkan masa penciptaan yang disimpan dengan masa kini dan mengembalikan data jika tidak tamat tempoh.

Jika kunci tidak wujud atau tamat tempoh, kami terus melaksanakan pertanyaan dan menyimpan hasil baharu yang ditetapkan dalam cache sebelum mengembalikannya.

Mudah!

Sekarang mari kita menukar "model_Index" ke model dengan cache yang expiring. Seperti yang berlaku, dengan "model_Documents," kita hanya perlu mengubah suai perisytiharan kelas dan menukar fasal "extends":

Mengenai masa tamat tempoh ... beberapa pertimbangan mesti dibuat. Kami menggunakan masa tamat tempoh berterusan (1 jam = 3,600 saat), demi kesederhanaan, dan kerana kami tidak mahu memodifikasi seluruh kod kami. Tetapi, kami dapat mengubahnya dengan mudah dalam banyak cara untuk membolehkan kami menggunakan masa berlakunya berlainan, satu untuk setiap model. Selepas itu kita akan melihat bagaimana.

Gambar rajah kelas untuk semua tugas kami adalah seperti berikut:

5. Pembatalan yang berbeza

Dalam setiap projek, saya pasti anda akan mempunyai masa tamat tempoh yang berbeza untuk hampir setiap model: dari beberapa minit hingga jam, atau bahkan hari.

Jika hanya kita boleh mempunyai masa tamat yang berlainan untuk setiap model, ia akan menjadi sempurna ... tetapi, tunggu! Kita boleh melakukannya dengan mudah!

Pendekatan yang paling langsung adalah untuk menambah hujah kepada pembina, jadi pembina baru untuk "model_ExpiringCache" akan menjadi yang berikut:

Kemudian, jika kita mahukan model dengan masa tamat 1 hari (1 hari = 24 jam = 1,440 minit = 86,400 saat), kita dapat melakukannya dengan cara ini:

Dan itu sahaja. Walau bagaimanapun, kelemahannya ialah kita mesti mengubah semua model data.

Cara lain untuk melakukannya adalah untuk mewakilkan tugas ke "app_AppConfig":

Dan kemudian tambahkan panggilan ke kaedah baru ini pada pembangun "model_ExpiringCache", seperti ini:

Kaedah terbaru ini membolehkan kita melakukan perkara mewah, seperti menggunakan nilai akhir yang berlainan untuk persekitaran pengeluaran atau pembangunan dengan cara yang lebih terpusat. Bagaimanapun, anda boleh memilih milik anda.

Di UML, projek total kelihatan seperti ini:

6. Beberapa Kaveat

Terdapat beberapa pertanyaan yang tidak dapat di cache. Yang paling jelas adalah mengubah suai pertanyaan seperti INSERT, DELETE atau UPDATE. Pertanyaan ini mesti tiba di pelayan pangkalan data.

Tetapi walaupun dengan pertanyaan SELECT, terdapat beberapa keadaan di mana sistem caching boleh menimbulkan masalah. Lihatlah pertanyaan seperti ini:

Pertanyaan ini memilih 10 spanduk rawak untuk zon "home" laman web kami. Ini bertujuan untuk menjana pergerakan dalam sepanduk yang ditunjukkan di rumah kami, tetapi jika kami menyembunyikan pertanyaan ini, pengguna tidak akan melihat apa-apa pergerakan sama sekali, sehingga data yang di-cache tamat.

Fungsi rand() tidak ditentukan (kerana bukan now() atau yang lain); jadi ia akan mengembalikan nilai yang berbeza pada setiap pelaksanaan. Jika kita menyembunyikannya, kita akan membekukan hanya satu daripada keputusan tersebut untuk semua tempoh caching, dan oleh itu melanggar fungsi.

Tetapi dengan pemfaktoran semula yang mudah, kita boleh mendapatkan manfaat caching dan menunjukkan pseudo-randomness:

Apa yang kita lakukan di sini adalah menyembunyikan lima puluh randaan spanduk rawak yang berbeza, dan memilihnya secara rawak. 50 SELECT akan kelihatan seperti ini:

Kami telah menambah keadaan malar pada pilihan, yang tidak mempunyai kos untuk pelayan pangkalan data tetapi menjadikan 50 kunci yang berbeza untuk sistem caching. Seorang pengguna perlu memuatkan halaman lima puluh kali untuk melihat semua konfigurasi spanduk yang berlainan; jadi kesan dinamik tercapai. Kos adalah lima puluh pertanyaan kepada pangkalan data untuk mengambil cache.

7. Penanda aras

Apa faedah yang boleh kita harapkan daripada sistem caching baru kita?

Pertama, mesti dikatakan bahawa, dalam prestasi mentah, kadangkala pelaksanaan baru kami akan berjalan lebih lambat daripada pertanyaan pangkalan data, khususnya dengan pertanyaan yang sangat mudah dan teroptimisasi. Tetapi bagi pertanyaan tersebut dengan bergabung, cache DBM kami akan berjalan lebih cepat.

Walau bagaimanapun, masalah yang kita selesaikan bukan prestasi mentah. Anda tidak akan mempunyai server pangkalan data ganti untuk ujian anda dalam pengeluaran. Anda mungkin mempunyai pelayan dengan beban kerja yang tinggi. Dalam keadaan ini, walaupun pertanyaan terpantas boleh berjalan dengan perlahan, tetapi dengan skema caching kami, kami tidak menggunakan pelayan, dan, sebenarnya, kami mengurangkan beban kerja. Jadi peningkatan prestasi sebenar akan datang dalam bentuk lebih petisi setiap kali disajikan.

Di laman web yang sedang saya sedang membangun, saya telah membuat penanda aras mudah untuk memahami manfaat caching. Pelayan sederhana: ia menjalankan Ubuntu 8.10 berjalan di atas AMD Athlon 64 X2 5600+, dengan 2 GB RAM dan cakera keras PATA yang lama. Sistem berjalan Apahce dan MySQL 5.0, yang datang dengan pengedaran Ubuntu tanpa penalaan.

Ujiannya adalah untuk menjalankan program penanda aras Apache (ab) dengan 1, 5 dan 10 pelanggan serentak memuat halaman 1,000 kali dari laman web pembangunan saya. Halaman sebenarnya adalah perincian produk yang tidak kurang dari 20 pertanyaan: isi menu, butiran produk, produk yang disyorkan, sepanduk, dll.

Keputusan tanpa cache adalah 4.35 p / s untuk 1 klien, 8.25 untuk 5 pelanggan, dan 8.29 untuk 10 klien. Dengan caching (expiration yang berbeza), hasilnya adalah 25.55 p / s dengan 1 klien, 49.01 untuk 5 pelanggan, dan 48.74 untuk 10 pelanggan.

Pemikiran Akhir

Saya telah menunjukkan kepada anda cara mudah untuk memasukkan caching ke dalam model data anda. Sudah tentu, terdapat banyak alternatif, tetapi ini adalah satu pilihan yang anda ada.

Kami telah menggunakan fail DBM tempatan untuk menyimpan data, tetapi ada alternatif yang lebih cepat yang mungkin anda pertimbangkan untuk meneroka. Beberapa idea untuk masa depan: menggunakan apc_store () fungsi APC sebagai sistem penyimpanan asas, memori yang dikongsi untuk data yang benar-benar kritikal, menggunakan memcached, dan lain-lain.

Saya berharap anda telah menikmati tutorial ini sebanyak yang saya menulis. Selamat caching!

Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.