jQueryTips by Tee++;

  • Home
  • Videos
  • jQueryTips's Fans

Coding like playing a piano.

Subscribe RSS

Search

Search

Sponsored

jQueryTips on Social

Followers

hide
Chainnnn Pang25441 Dexteri@n Ton KanexKane Supachai JustDoIt Rut petchy Tophit anndrew Webmaster

Categories

hide
  • Programing
  • Database
  • Framework
  • Mobile Development
    • Titanium Mobile
    • PhoneGap
  • Life

Tag Cloud

hide
PHP JavaScript MySQL XML Mobile jQuery CodeIgniter Hash php xml Debug Library Zend API Events Plugins Facebook Basic Registration Tool Twitter Search Swath Performance ffmpeg YouTube Web Service Yahoo! NoSQL MongoDB PhoneGap

Recent Posts

hide
  • ใครๆ ก็มี Utils เอางี้เราเพิ่ม Utils ให้กับ CodeIgniter กันบ้าง
  • แนะนำ JS.Class (A JavaScript class library)
  • PHP กับ OOP มาปูพื้นฐานกันเสียใหม่ก่อน
  • ประกาศข่าวสารจาก Admin
  • JSFIDDLE สุดยอด Tools สำหรับการ Debug การเขียนตัวอย่างโคด
  • Titanium Mobile + HTML + Native Scrolling
  • HTML to PDF ตัวไหนดี วันนี้ผมมีคำตอบ ...
  • ทำความรู้จักกับ JavaScript Pattern ในเชิงลึก
  • ผมบอกคุณแล้ว .... คนโง่ก็ Code ได้
  • วันนี้ มาออกแบบ Web Service ด้วย PHP กันเถอะ (CodeIgniter Version)

Recent Comments

hide
  • เยดดดด
  • มีตัวอย่าง CI แบบ เพียว ๆ ไหมครับ
  • ขอบคุณมากครับ เห็นแนวทางได้ชัดขึ้นจากตอนแรก ที่งงไปหมด ไม่รู้อะไรเป็นอะไร
  • ขอบคุณมากเลยครับท่าน....แหม่เพิ่มกำลังใจให้ผมได้เยอะเลย ตอนนี้กำลังฝึกงานอยู่ที่แห่งนึงในหาดใหญ่ พร้อมเด็กจากอีกสถาบันนึง 3 คน เราคิดว่าเรา…
  • Very good blog, I really like ~

Links

hide
  • OSCOOL
  • Architeture & Performance
  • Video and TV
  • Appcelerator
  • PhoneGap
  • Home
  •  » Blogs

[CI Day 9] Query Caching Modify

Jan 23, 2009 3:36:05 PM | 8 Comments | in Framework | CodeIgniter
สวัสดีมาต่อเรื่อง CI กันแล้วครับ ตอนนี้ผมตัดสินใจละ ว่าจะเขียนถึง Day 10 เท่านั้นแล้วก็จะกลับเข้ามาสู่ ภาคของ jQuery ต่อ เพราะว่าตั้งแต่ jQuery ออก V.1.3 ผมก็ไม่ได้อัพเดท อะไรเลย ทั้งๆ ที่มีของเล่นใหม่ๆ เพิ่มมาพอสมควร และที่สำคัญ ผมขี้เกียจเขียนเรื่อง CI ละ มันต้องอธิบาย กันลึกเกิน ไปซึ่งผม ก็เขียนอธิบาย ไม่ค่อยเก่งเสียด้วย แต่ถ้ามีอะไรก็ถามกันเข้ามาได้ครับ

เออ ช่วงนี้เห็นหลายคนขอ M ผมเข้ามาเยอะเหลือเกิน ยังไงผมก็จะให้ไว้ในกระทู้นี้ละกัน d n a b o a r d @ g m a i l . c o m ยังไงแวะเวียนมาแลกเปลี่ยนความรู้กันได้เลย

วันนี้เรื่องที่จะมาเล่า คือเรื่องการทำ Caching ของ CI ครับ แน่นอนครับ ทำเวบสมัยนี้ มันไม่เหมือนเมื่อก่อนแล้ว มันมีเทคนิคที่ พัฒนาไปมาก ในการทำเวบ เรื่องที่ผมใส่ใจมากเป็นพิเศษ นอกเหนือจาก Structure แล้วก็คือ Performance นี่ล่ะครับ และสิ่งที่ Developer ทำกันมากที่สุด ในการ Tuning Performance นั่นก็คือ การทำ Caching ครับ ผมยกตัวอย่างเช่น Facebook.com

รู้มั้ยครับ facebook เองเขียนด้วย PHP ส่วน DB ก็ยังใช้ MySQL นี่ล่ะ แต่ทำไม มันถึงรองรับ คนได้มากเหลือเกิน ซึ่งจะว่ากันไปจริงๆ แล้วมันมีหลายเรื่องทั้ง Load Balance, Reverse Proxy แล้วก็ องค์ประกอบของ Hardware แต่เรื่องที่ น่าสนใจคือ FB มีการ Access DB น้อยมาก ซึ่งเราเองรู้กันดีอยู่แล้วว่า MySQL เนี่ย มันไม่ค่อยจะดีนัก ต่อเวบที่มี คนเยอะๆ มันอ่อนแอเหลือเกิน

ดังนั้นแทนที่ FB จะทำการ Access เข้า DB ตรงๆ อยู่ตลอดเวลา ก็เปลี่ยนมาออกแบบให้ Access ป่าน Memcached ซึ่งเป็นการ Cached ใน Fast Memory (**Data จะหายเมื่อมีการ Reboot**) ด้วยวิธีนี้จะทำให้การ Access DB ลดน้อยลง จนเราแปลกใจ และเป็นสาเหตุหลักที่ทำให้เวบมี Down Time ต่ำมากๆ

อ้างอิง: Facebook Scalability

แต่ด้วยข้อจำกัดของ Server ที่เพื่อนๆ ใช้มันทำให้การที่ผมจะไปพูดถึง Cached ระดับ Memory เช่น APC, Memcached นั้น ดูจะแคบไปหน่อย ผมก็เลยขอเลี่ยงไปพูดถึง Cache ในระดับ Flat File แทน ซึ่งน่าจะทำกันได้ในทุกๆ Server อยู่แล้ว

(ผมไม่ขอพูดถึง Caching ทีมีใน CI นะครับ เพราะว่าผมไม่ปลื้มเท่าไหร่ ถ้าสนใจก็อ่านใน manual น่าจะเข้าใจได้ไม่ยาก)

เอาล่ะไม่รู้จะเกริ่นยาวไปถึงไหน มาเริ่มกันเลยดีกว่าครับ....

ในการออกแบบ Structure ของวันนี้เราจะพูดถึงเรื่องหลักๆ 2 เรื่องคือ
1. การออกแบบ Folder Structure (Node)
2. การออกแบบ Cache แบบ GZIP

เรื่องแรกก่อน (วันนี้สงสัยไม่จบง่ายๆ -*-)

เพื่อนๆ รู้มั้ยครับ การจัดเก็บไฟล์ลงใน Dir แต่ละ Dir นั้นก็มีข้อจำกัดเหมือนกัน ไม่ใช่เอะอะๆ ยัดเก็บ Dir เดียว พรวดๆ ถ้าทำแบบนั้นมันจะเกิดปัญหาในท้ายที่สุดคือ Node เต็ม ซึ่งตามที่เคยรู้มามันควรจะเป็นราวๆ 60,000 ต่อ 1 ชั้นราวๆ นี้มั้ง (จริงๆ ยัดลงไปได้อีกเยอะ แต่มันจะช้าลงเรื่อยๆ จนตายไปในที่สุด)

ตัวอย่างการจัดเก็บที่ดีก็คือ Flickr ลองไปสังเกตุ Path ของรูปดูสิครับ จะเห็นได้ว่า มีการแบ่งเป็น Farm และ Sup Path ไว้อย่างดีเลยทีเดียว

ทีนี้ผมจะมาลอง algorithm ง่ายๆก่อน โดยจะใช้แค่ Dir ขวางเพียงชั้นเดียว โดยทำ Hashing ด้วย CRC32
function crc32_hash($key, $max=5000)

{

  return (crc32($key) & 0x7fffffff) % $max;

}


เขียนเท่านี้ล่ะ ผมไม่ขออธิบายถึง CRC32 Algorithm นะครับ แต่ว่าเพื่อนๆ ไปหาข้อมูลเพิ่มได้จากพี่ Goo นั่นล่ะ ผมจะขอบอกแค่ว่าจาก Function ด้านบนสิ่งที่เพื่อนๆจะได้กลับมาก็คือ ข้อมูล 1 ชุดที่เป็นตัวเลข ไม่เกิน 5,000 โดยเราเอาอะไรใส่เข้าไปก็ได้ เช่น
echo crc32_hash('Tee++;');

// output 322


ตัวเลขที่ออกมานั่นล่ะครับ คือส่งที่ผมจะเอาไปสร้าง node
/main_dir/322/

เห็นมั้ยครับเท่านี้ผมก็จะได้ Dir มาขวาง 1 ชั้นแล้ว ไม่จำเป็นต้องเอาไปเก็บที่ main_dir อย่างเดียว โดยที่ Folder ในชั้นที่ขวางนี้จะมีไม่เกิน 5,000 เพราะผมตั้งเอาไว้แค่นั้น

นอกจากนี้ Algorithm ของการทำ Hashing Dir ยังมีอีกมากมายครับ ลองหาเอาจากใน Net ดูกันเอา ส่วนตอนี้ผมจะเข้าเรื่องที่ผมจะเขียนก่อนล่ะ

เรื่องที่สอง

มาเขียน lib caching เพื่อไป plug ใน CI กัน
[CI]/application/libraries/Caching.php

<?php if ( ! defined(‘BASEPATH’)) exit(‘No direct script access allowed’);
class Caching {
 
        var $CI;
        var $_parent = ‘cache/’;
        var $_maxDir = 5000;
        var $_path;
 
        function __construct()
        {
                $this->CI =& get_instance();
                $this->CI->load->helper(‘node’);
        }
 
        function setParent($parent)
        {
                if (substr($parent, -1) != "/")
                        $parent = $parent.‘/’;
                $this->_parent = $parent;
        }
 
        function setMaxDir($max)
        {
                $this->_maxDir = $max;
        }
 
        function setDir($subDir, $str=null)
        {
                if (is_null($str) || empty($str)) return;
                $hash = ’server_’.crc32_hash($str, $this->_maxDir).‘/’;
                $this->_path = APPPATH.$this->_parent.$subDir.‘/’.$hash;
        }
        #### end if use key as string #####
 
        function store($key, $data, $ttl=86400)
        {
                if (!$key || !$data) return;
                mkdirs(dirname($this->getFileName($key)));
                $h = @gzopen($this->getFileName($key), ‘w’);
                if (!$h) return;
 
                $data = serialize(array(time() + $ttl, $data));
                if (gzwrite($h, $data) === false)       return;
                gzclose($h);
        }
 
        function fetch($key)
        {
                if (!$key) return;
                $filename = $this->getFileName($key);
                if (!file_exists($filename) || !is_writeable($filename)) return false;
 
                $h = @gzopen($filename, ‘r’);
                if (!$h) return;
 
                while (!gzeof($h))
                        $data .= gzread($h, 4096);
 
                gzclose($h);
 
                $data = unserialize($data);
                if (!$data || time() > $data[0])
                {
                         $this->delete($key);
                         return false;
                }
 
                return $data[1];
        }
 
        function delete($key)
        {
                if (!$key) return;
                $filename = $this->getFileName($key);
                if (file_exists($filename))
                        return unlink($filename);
                else
                        return false;
        }
 
        function getFileName($key)
        {
                return $this->_path.md5($key).‘.gz’;
  }
 
}
?>

เอาล่ะ ผมจะมาอธิบายว่าในแต่ละ method ทำหน้าที่อะไรกันบ้าง
1. construct
ใช้สำหรับเรียกค่าต่างๆ ของ CI เข้ามาใช้กันและทำการ load helper node มาใช้ พอดีไอ้ function ชุดแรกผมไปเขียนใน helper ที่ชื่อว่า node น่ะครับ

2. setParnet
เป็นตัวกำหนด Dir หลักที่จะ ทำการใส่ data cache ลงไป

3. setMaxDir
เป็นตัวกำหนดว่าจะ hash dir ย่อยทั้งหมดภายใต้ main dir กี่ folder

4. setDir
เป็นตัว hash dir ย่อยภายใต้ main dir อีกที โดยจะรับค่า string เข้ามาทำการ hash

5. strore
เป็นตัวเก็บข้อมูลเพื่อทำการ Caching โดยจะระบุ key, data, expiration โดยจะ write file เป็น .gz เพื่อบีบขนาดให้เล็กที่สุด ข้อมูลภายในจะถูกเก็บเป็น array โดย

index:0 จะเก็บ current time + expire time
index:1 data ในรูปแบบ data ที่เข้ามาปกติ โดย data จะอยู่ในรูปแบบใดก็ได้ เช่น string, array, object

จากนั้นจะทำการ Write ข้อมูลแบบ PHP Serialize เพื่อแปลง Array เป็น String ชุดเดียว

6. fetch
เป็นตัวดึงข้อมูลกลับออกมาจาก cache โดยระบุ key ค้นหาเข้าไป การดึงข้อมูลกลับ จะทำการเช็คที่ index:0 ก่อนคือ Expiration ถ้าหมดอายุก็จะทำการ delete cache ทิ้งและ return false กลับมา (miss cached) ถ้าไม่ใช่ก็จะคืน data ที่ cached เข้าไปกลับมา

7. delete
เป็นตัว ลบข้อมูลของ cached ตาม key index

8. getFileName
อันนี้จะถูกใช้แค่ระหว่าง method เป็นการ hash ชื่อไฟล์ที่จะ cache ด้วย md5 โดยจะ return กลับไปพร้อมๆ กับ path

เอาล่ะๆ จบแล้วสำหรับขั้นตอนการเขียน ตอนนี้มาดูขั้นตอนการใช้งานจริงๆ กันเลยดีกว่า ส่วนมาก caching นี้ผมจะเอามา cache Query ที่อยู่ใน model เสียเป็นส่วนใหญ่คือ ไม่ให้ดึง data จาก DB ตลอดเวลา แต่ให้ดึงเอาจาก cache ในกรณีที่ data ไม่มีการเปลี่ยนแปลง

สมมุติ ผมมี model::User_model->getUserData($user_id) เพื่อที่จะดึงข้อมูลของสมาชิก แทนที่ผมจะ Query db ตรงๆ ผมก็จะเขียนแบบนี้แทน
$this->caching->setDir(‘user_dir’, $user_id)
if (!$row = $this->caching->fetch($user_id))
{
  $sql = "SELECT ……….";
  $row = "FETCH YOUR QUERY";
  $this->caching->store($user_id, $row, 3600);
}

เป็นอันจบครับสำหรับ lib:caching ที่เขียนเองขึ้นมา ส่วนการจัดการ cache ผมจะ ไกด์คร่าวๆ ว่ามันควรจะเป็นยังไงนะครับ มันมีผังการเดินทางของมันอยู่ ตัวอย่างเช่น

1. ถ้า fetch ข้อมูล จาก key เจอ จะ ใช้ data จาก cache
2. ถ้า fetch ข้อมูล จาก key ไม่เจอ ก็จะใช้ data สดๆ แต่ก็ทำ การ store cache ไปพร้อมๆกัน

3. ถ้ามีการ update data ก็จะทำการลบ cache ตาม key เพื่อให้ข้อมูลมีความสดใหม่ และวนกลับไปยังข้อ 2

สรุป method ใน model ที่ควรจะมีความเกี่ยวเนื่องกับระบบ cache มันก็ควรจะมีพวกเรื่อง

getData, updateData

อืมม์ เรื่องการ manage cache ให้ได้ข้อมูลสดอยู่ตลอดไม่ใช่เรื่องง่ายครับ แต่ผมคิดว่ามันคงไม่ยากเกินไปหรอกครับ และที่สำคัญ ถ้าเพื่อนๆ ทำความเข้าใจกับ lib:caching ที่ผมเขียนขึ้นมาตัวนี้ได้แล้ว จะเอา method ไปแก้ไขเป็น memcached หรือ apc อะไรก็ไม่ลำบากแล้วล่ะครับ

วันนี้เนื้อหาค่อนข้างหนักพอสมควร ผมเองก็ปวดหัวละ ขอพอก่อนละกัน สวัสดีครับบ

twitter stumbleupon delicious digg facebook

8 comments

Add comment Load previous All comments

Leave a comment

Post Comment

Powered by OSCOOL. You may view this on RSS or ATOM.

OSCOOL

  • Twitter
  • Facebook
  • Next