Tăng tốc truy vấn MySQL với PHP Memcached: Giải pháp cache hiệu quả

Tăng tốc truy vấn MySQL với PHP Memcached: Giải pháp cache hiệu quả

Memcached là một hệ thống lưu trữ đệm (cache) phân tán mạnh mẽ, giúp tăng tốc hiệu suất ứng dụng web bằng cách lưu trữ dữ liệu tạm thời trong RAM. Nhờ tận dụng bộ nhớ truy cập ngẫu nhiên (RAM) của máy chủ, Memcached mang lại tốc độ xử lý vượt trội, thậm chí nhanh hơn nhiều lần so với các ổ SSD hiệu suất cao nhất.

Khi người dùng truy cập ứng dụng web lần đầu, Memcached sẽ lấy dữ liệu từ nguồn lưu trữ chính và lưu lại dưới dạng key-value. Các lần truy vấn sau đó sẽ được xử lý tức thì, giảm tải đáng kể cho hệ thống bằng cách hạn chế thao tác đọc/ghi trên ổ đĩa, giúp tối ưu hiệu suất tổng thể. Đây chính là lý do Memcached trở thành một trong những giải pháp caching phổ biến nhất hiện nay.

DataOnlinesex hướng dẫn bạn sử dụng thư viện php-memcache để lưu trữ và truy xuất dữ liệu MySQL bằng PHP trên Ubuntu 20.04. Sau đó, dữ liệu sẽ được xuất dưới định dạng JSON (JavaScript Object Notation), giúp dễ dàng xử lý và hiển thị trên các ứng dụng web hiện đại.

Yêu cầu

Để theo dõi hướng dẫn này, hãy đảm bảo rằng bạn đã có:

Cài đặt thư viện php-memcache

Đầu tiên, SSH vào máy chủ của bạn và cài đặt thư viện php-memcache. Đây là một module PHP cho phép bạn sử dụng các tính năng của Memcached bên trong mã PHP.

$ sudo apt update
$ sudo apt install -y php-memcache

Khởi động lại máy chủ Apache để tải thư viện php-memcache.

$ sudo systemctl restart apache2

Thiết lập cơ sở dữ liệu test_db

Tiếp theo, kết nối tới máy chủ MySQL với tư cách root.

$ sudo mysql -u root -p

Nhập mật khẩu root của MySQL và nhấn Enter để tiếp tục. Khi bạn nhận được dấu nhắc mysql>, chạy lệnh dưới đây để tạo cơ sở dữ liệu test_db.

mysql> CREATE DATABASE test_db;

Tạo một user không phải root có tên test_db_user cho máy chủ MySQL. Nhớ thay EXAMPLE_PASSWORD bằng một mật khẩu mạnh. Bạn sẽ sử dụng các thông tin đăng nhập này để kết nối MySQL từ PHP.

mysql> CREATE USER 'test_db_user'@'localhost' IDENTIFIED WITH mysql_native_password BY 'EXAMPLE_PASSWORD';
mysql> GRANT ALL PRIVILEGES ON test_db.* TO 'test_db_user'@'localhost';
mysql> FLUSH PRIVILEGES;

Nếu bạn sử dụng MariaDB thay thế cho MySQL, thay đổi lệnh trên theo cú pháp dưới đây để tạo user test_db_user:

MariaDB> GRANT ALL PRIVILEGES on test_db.* TO 'test_db_user'@'localhost' identified by 'EXAMPLE_PASSWORD';

Chuyển sang cơ sở dữ liệu test_db.

mysql> USE test_db;

Tiếp theo, tạo bảng products. Bảng này sẽ lưu trữ dữ liệu một cách lâu dài trong cơ sở dữ liệu MySQL của bạn. Vì MySQL lưu dữ liệu vào ổ đĩa của máy chủ, nên hiệu năng có thể giảm khi bạn thêm hàng ngàn sản phẩm hoặc khi ứng dụng web của bạn trở nên “viral” và có nhiều người truy cập cùng lúc. Sau này, bạn sẽ tạo cache cho bảng này bằng máy chủ Memcached trong một file PHP.

Hiện tại, chỉ cần thiết lập bảng products.

mysql> CREATE TABLE products
       (
           product_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
           product_name VARCHAR(50),
           retail_price DOUBLE
       ) ENGINE = InnoDB;

Chèn một số bản ghi vào bảng products.

mysql> INSERT INTO products(product_name, retail_price) VALUES ('LEATHER JACKET', '99.95');
mysql> INSERT INTO products(product_name, retail_price) VALUES ('LED MOUSE', '22.65');
mysql> INSERT INTO products(product_name, retail_price) VALUES ('MOUSE PAD', '4.95');
mysql> INSERT INTO products(product_name, retail_price) VALUES ('PURE COTTON BUDS', '2.85');

Chạy câu lệnh SELECT để kiểm tra xem dữ liệu đã được chèn thành công hay chưa.

mysql> SELECT
       product_id,
       product_name,
       retail_price
       FROM products;

Kết quả hiển thị sẽ giống như sau:

+------------+------------------+--------------+
| product_id | product_name     | retail_price |
+------------+------------------+--------------+
|          1 | LEATHER JACKET   |        99.95 |
|          2 | LED MOUSE        |        22.65 |
|          3 | MOUSE PAD        |         4.95 |
|          4 | PURE COTTON BUDS |         2.85 |
+------------+------------------+--------------+
4 rows in set (0.00 sec)

Thoát khỏi giao diện dòng lệnh MySQL.

mysql> QUIT;

Bạn đã xác định cơ sở dữ liệu, tạo bảng mẫu và chèn dữ liệu. Tiếp theo, bạn sẽ tạo một script PHP để kết nối đến máy chủ, truy xuất và hiển thị dữ liệu dưới định dạng JSON.

Tạo File PHP

Sử dụng nano để tạo một file mới /var/www/html/products.php trong thư mục gốc của máy chủ web.

$ sudo nano /var/www/html/products.php

Mở file và bắt đầu bằng một thẻ <?php mới, sau đó thêm header "Content-Type:application/json" để thông báo cho các client web rằng dữ liệu trả về sẽ ở định dạng JSON.

<?php

header("Content-Type:application/json");

Tiếp theo, mở một khối try { ... } và khai báo các biến cơ sở dữ liệu. Khớp các biến với tên cơ sở dữ liệu, user và mật khẩu mà bạn đã tạo ở bước trước.

try {

    $db_name     = 'test_db';
    $db_user     = 'test_db_user';
    $db_password = 'EXAMPLE_PASSWORD';
    $db_host     = 'localhost';

Tiếp theo, khởi tạo một đối tượng Memcache mới và gọi phương thức addServer để kết nối tới máy chủ Memcached trên cổng 11211.

$memcache = new Memcache();
$memcache->addServer("127.0.0.1", 11211);

Sau đó, định nghĩa một câu lệnh SQL SELECT. Bạn sẽ sử dụng lệnh này để truy vấn bảng products và lấy các mục được lưu trữ trong máy chủ MySQL.

$sql = 'SELECT
          product_id,
          product_name,
          retail_price
          FROM products
         ';

Tiếp theo, tạo một tên mới cho key của Memcached bằng cách truyền giá trị của câu lệnh SQL vào hàm md5(). Điều này tạo ra một key nhỏ gọn, dễ dàng để kiểm tra nếu có vấn đề phát sinh.

$key = md5($sql);

Tiếp theo, thêm đoạn code để kiểm tra xem máy chủ Memcached có chứa key với tên vừa tạo hay không. Nếu giá trị key không phải là null (!= null), trả về dữ liệu cache từ máy chủ Memcached. Sau đó, sử dụng câu lệnh $response['Memcache Data'] để đánh dấu dữ liệu khi script trả về từ cache.

$cached_data = $memcache->get($key);

    $response = [];

    if ($cached_data != null) {

        $response['Memcache Data'] = $cached_data;

Nếu không có dữ liệu cache, sử dụng đoạn code bên dưới để truy xuất dữ liệu từ máy chủ MySQL bằng thư viện PDO. Sau khi lấy dữ liệu từ cơ sở dữ liệu MySQL lần đầu, sử dụng lệnh $memcache->set($key, $products, false, 5); để cache dữ liệu trong 5 giây. Bạn có thể tăng giá trị TTL (thời gian sống) này tùy theo logic nghiệp vụ của bạn. Sau đó, sử dụng câu lệnh $response['MySQL Data'] để đánh dấu dữ liệu được trả về từ MySQL.

} else {

      $pdo = new PDO("mysql:host=" . $db_host  . ";dbname=" . $db_name, $db_user, $db_password);
      $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
      $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

      $stmt = $pdo->prepare($sql);
      $stmt->execute();

      $products = [];

      while (($row = $stmt->fetch(PDO::FETCH_ASSOC)) !== false) {
          $products[] = $row;
      }

      $memcache->set($key, $products, false, 5);

      $response['MySQL Data'] =  $products;

  }

Cuối cùng, xuất dữ liệu đã được mã hóa dưới dạng JSON, lấy dữ liệu từ MySQL hoặc từ cache Memcached. Trong trường hợp có lỗi PDO, bắt lỗi và hiển thị thông báo lỗi.

 echo json_encode($response, JSON_PRETTY_PRINT) . "\n";

} catch(PDOException $e) {
    $error = [];
    $error['message'] = $e->getMessage();
    echo json_encode($error, JSON_PRETTY_PRINT) . "\n";
}

Khi bạn đã nhập đầy đủ nội dung vào file /var/www/html/products.php, file của bạn sẽ giống với nội dung dưới đây:

<?php

header("Content-Type:application/json");

try {

    $db_name     = 'test_db';
    $db_user     = 'test_db_user';
    $db_password = 'EXAMPLE_PASSWORD';
    $db_host     = 'localhost';

    $memcache = new Memcache();
    $memcache->addServer("127.0.0.1", 11211);

    $sql = 'SELECT
            product_id,
            product_name,
            retail_price
            FROM products
           ';

    $key = md5($sql);

    $cached_data = $memcache->get($key);

    $response = [];

    if ($cached_data != null) {

        $response['Memcache Data'] = $cached_data;

    } else {

        $pdo = new PDO("mysql:host=" . $db_host  . ";dbname=" . $db_name, $db_user, $db_password);
        $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

        $stmt = $pdo->prepare($sql);
        $stmt->execute();

        $products = [];

        while (($row = $stmt->fetch(PDO::FETCH_ASSOC)) !== false) {
            $products[] = $row;
        }

        $memcache->set($key, $products, false, 5);

        $response['MySQL Data'] =  $products;

    }

    echo json_encode($response, JSON_PRETTY_PRINT) . "\n";

} catch(PDOException $e) {
    $error = [];
    $error['message'] = $e->getMessage();
    echo json_encode($error, JSON_PRETTY_PRINT) . "\n";
}

Lưu và đóng file khi đã hoàn tất bằng cách nhấn Ctrl + X, sau đó nhấn YEnter. Tiếp theo, kiểm tra xem file có hoạt động đúng như mong đợi không.

Kiểm tra dữ liệu Cache

Gọi file products.php hai lần để kiểm tra đầu ra. Để làm điều này, chạy lệnh curl dưới đây:

$ curl http://localhost/products.php

Khi bạn yêu cầu dữ liệu lần đầu, dữ liệu sẽ được lấy từ máy chủ MySQL ("MySQL Data": [...]) như hiển thị dưới đây:

 {
    "MySQL Data": [
        {
            "product_id": 1,
            "product_name": "LEATHER JACKET",
            "retail_price": 99.95
        },
        {
            "product_id": 2,
            "product_name": "LED MOUSE",
            "retail_price": 22.65
        },
        {
            "product_id": 3,
            "product_name": "MOUSE PAD",
            "retail_price": 4.95
        },
        {
            "product_id": 4,
            "product_name": "PURE COTTON BUDS",
            "retail_price": 2.85
        }
    ]
}

Chạy lại lệnh curl trước khi key của Memcached hết hạn:

$ curl http://localhost/products.php

Lần này, dữ liệu sẽ được lấy từ Memcache ("Memcache Data": [...]):

{
   "Memcache Data": [
       {
           "product_id": 1,
           "product_name": "LEATHER JACKET",
           "retail_price": 99.95
       },
       {
           "product_id": 2,
           "product_name": "LED MOUSE",
           "retail_price": 22.65
       },
       {
           "product_id": 3,
           "product_name": "MOUSE PAD",
           "retail_price": 4.95
       },
       {
           "product_id": 4,
           "product_name": "PURE COTTON BUDS",
           "retail_price": 2.85
       }
   ]
 }

Như bạn có thể thấy từ các đầu ra trên, file PHP của bạn hoạt động như mong đợi và bạn đã thành công trong việc cache dữ liệu với máy chủ Memcached.

Kết luận

Trong hướng dẫn này, bạn đã triển khai thành công thư viện php-memcache để cache dữ liệu MySQL và xuất dưới dạng JSON trên máy chủ Ubuntu 20.04. Với phương pháp này, bạn có thể tối ưu hiệu suất ứng dụng web bằng cách giảm tải truy vấn trực tiếp đến MySQL, giúp trang web động chạy nhanh hơn và phản hồi mượt mà hơn. Hãy áp dụng logic cache này để cải thiện tốc độ và hiệu suất cho các hệ thống web sử dụng MySQL làm backend.

Để lại một bình luận

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *