Năm vấn đề chung về cơ sở dữ liệu trên PHP

Và làm thế nào để tránh chúng

Hãy khám phá năm vấn đề về cơ sở dữ liệu phổ biến xảy ra trong các ứng dụng PHP -- gồm thiết kế lược đồ cơ sở dữ liệu, truy cập cơ sở dữ liệu và mã logic nghiệp vụ sử dụng cơ sở dữ liệu -- cũng như các giải pháp của chúng.

Jack Herrington, Tổng biên tập, Code Generation Network

Jack D. Herrington là kỹ sư phần mềm cao cấp với hơn 20 năm kinh nghiệm. Ông là tác giả của ba cuốn sách: Code Generation in Action, Podcasting Hacks và PHP Hacks. Ông cũng đã viết hơn 30 bài báo.



08 01 2010

Nếu chỉ có một cách để sử dụng cơ sở dữ liệu chính xác ...

Bạn có thể tạo thiết kế cơ sở dữ liệu, truy cập cơ sở dữ liệu và mã logic nghiệp vụ PHP đặt trên đầu trang của nó theo một số cách nào đó và bạn thường kết thúc khi nhận thấy nó sai. Bài viết này minh họa năm vấn đề thường gặp trong việc thiết kế cơ sở dữ liệu, trong mã PHP truy cập các cơ sở dữ liệu và cách sửa chữa những vấn đề này khi bạn bắt gặp chúng.

Vấn đề 1: Sử dụng MySQL trực tiếp

Khi sử dùng các hàm mysql_ để truy cập trực tiếp vào cơ sở dữ liệu có một vấn đề phổ biến là mã PHP đã cũ. Liệt kê 1 chỉ ra cách truy cập trực tiếp vào cơ sở dữ liệu.

Liệt kê 1. Access/get.php
<?php
function get_user_id( $name )
{
  $db = mysql_connect( 'localhost', 'root', 'password' );
  mysql_select_db( 'users' );

  $res = mysql_query( "SELECT id FROM users WHERE login='".$name."'" );
  while( $row = mysql_fetch_array( $res ) ) { $id = $row[0]; }

  return $id;
}

var_dump( get_user_id( 'jack' ) );
?>

Hãy lưu ý việc sử dụng hàm mysql_connect để truy cập vào cơ sở dữ liệu. Cũng chú ý truy vấn trong đó có dùng sự liên kết chuỗi để bổ sung tham số $name vào truy vấn đó.

Kỹ thuật này có hai sự lựa chọn tốt: mô đun PEAR DB và các lớp PHP Data Objects (PDO-Các đối tượng dữ liệu PHP). Cả hai đều cung cấp sự trừu tượng từ việc lựa chọn của một cơ sở dữ liệu cụ thể. Do đó, mã của bạn có thể chạy mà không cần điều chỉnh quá nhiều trên IBM® DB2®, MySQL, PostgreSQL, hoặc cơ sở dữ liệu khác bất kỳ mà bạn muốn kết nối đến.

Các giá trị khác trong việc sử dụng các tầng trừu tượng của mô đun PEAR DB và PDO là bạn có thể sử dụng toán tử ? trong các câu lệnh SQL của bạn. Việc này làm cho SQL dễ dàng bảo trì hơn và bảo vệ ứng dụng của bạn khỏi các cuộc tấn công nội xạ SQL.

Mã thay thế khi sử dụng PEAR DB được hiển thị dưới đây.

Liệt kê 2. Access/get_good.php
<?php
require_once("DB.php");

function get_user_id( $name )
{
  $dsn = 'mysql://root:password@localhost/users';
  $db =& DB::Connect( $dsn, array() );
  if (PEAR::isError($db)) { die($db->getMessage()); }

  $res = $db->query( 'SELECT id FROM users WHERE login=?',
  array( $name ) );
  $id = null;
  while( $res->fetchInto( $row ) ) { $id = $row[0]; }

  return $id;
}

var_dump( get_user_id( 'jack' ) );
?>

Chú ý rằng tất cả các đề cập trực tiếp của MySQL đã diễn ra, trừ chuỗi kết nối cơ sở dữ liệu trong $dsn. Ngoài ra, chúng ta sử dụng biến $name trong SQL thông qua toán tử ?. Sau đó, dữ liệu với truy vấn này được gửi đi thông qua array ở cuối phương thức query().


Vấn đề 2: Không sử dụng chức năng tăng tự động

Giống như hầu hết các cơ sở dữ liệu hiện đại, MySQL có khả năng tạo các trình nhận dạng (identifier) duy nhất tăng tự động trên một cơ sở cho mỗi bản ghi. Mặc dù vậy, chúng ta vẫn thấy mã lần đầu tiên chạy một lệnh SELECT để tìm mã nhận dạng (id) tối đa, sau đó bổ sung thêm một vào id đó, cũng như một bản ghi mới. Liệt kê 3 cho thấy một lược đồ mẫu bad (xấu).

Liệt kê 3. Badid.sql
DROP TABLE IF EXISTS users;
CREATE TABLE users (
  id MEDIUMINT,
  login TEXT,
  password TEXT
);

INSERT INTO users VALUES ( 1, 'jack', 'pass' );
INSERT INTO users VALUES ( 2, 'joan', 'pass' );
INSERT INTO users VALUES ( 1, 'jane', 'pass' );

Trường id ở đây được quy định đơn giản là một số nguyên. Vì vậy, mặc dù nó sẽ là duy nhất, chúng ta có thể thêm vào bất kỳ giá trị nào mà chúng ta muốn, như đã chỉ ra trong câu lệnh INSERT tiếp theo câu lệnh CREATE. Liệt kê 4 chỉ ra mã PHP bổ sung thêm users (những người sử dụng) vào kiểu lược đồ này.

Liệt kê 4. Add_user.php
<?php
require_once("DB.php");

function add_user( $name, $pass )
{
  $rows = array();

  $dsn = 'mysql://root:password@localhost/bad_badid';
  $db =& DB::Connect( $dsn, array() );
  if (PEAR::isError($db)) { die($db->getMessage()); }

  $res = $db->query( "SELECT max(id) FROM users" );
  $id = null;
  while( $res->fetchInto( $row ) ) { $id = $row[0]; }

  $id += 1;

  $sth = $db->prepare( "INSERT INTO users VALUES(?,?,?)" );
  $db->execute( $sth, array( $id, $name, $pass ) );

  return $id;
}

$id = add_user( 'jerry', 'pass' );

var_dump( $id );
?>

Mã trong add_user.php đầu tiên thực hiện một truy vấn để tìm ra giá trị tối đa của id. Sau đó tệp này chạy một câu lệnh INSERT với giá trị id cộng thêm một. Mã này có thể không chạy thành công trong các điều kiện ganh đua (race) trên các máy chủ có một tải nặng. Hơn nữa, nó không hiệu quả.

Vì vậy, sự thay thế là gì? Sử dụng tính năng tăng tự động trong MySQL để tạo các ID duy nhất cho mỗi lần chèn tự động. Lược đồ cập nhật được hiển thị bên dưới.

Liệt kê 5. Goodid.php
DROP TABLE IF EXISTS users;
CREATE TABLE users (
  id MEDIUMINT NOT NULL AUTO_INCREMENT,
  login TEXT NOT NULL,
  password TEXT NOT NULL,
  PRIMARY KEY( id )
);

INSERT INTO users VALUES ( null, 'jack', 'pass' );
INSERT INTO users VALUES ( null, 'joan', 'pass' );
INSERT INTO users VALUES ( null, 'jane', 'pass' );

Chúng ta thêm cờ NOT NULL để chỉ ra rằng các trường này không rỗng (null). Chúng ta cũng đã bổ sung cờ AUTO_INCREMENT để chỉ thị rằng trường này tăng tự động, cũng như cờ PRIMARY KEY để chỉ thị trường nào là id. Những thay đổi này cho phép tăng một chút tốc độ. Liệt kê 6 cho thấy mã PHP đã cập nhật, mã này chèn users (những người dùng) vào bảng.

Liệt kê 6. Add_user_good.php
<?php
require_once("DB.php");

function add_user( $name, $pass )
{
  $dsn = 'mysql://root:password@localhost/good_genid';
  $db =& DB::Connect( $dsn, array() );
  if (PEAR::isError($db)) { die($db->getMessage()); }

  $sth = $db->prepare( "INSERT INTO users VALUES(null,?,?)" );
  $db->execute( $sth, array( $name, $pass ) );

  $res = $db->query( "SELECT last_insert_id()" );
  $id = null;
  while( $res->fetchInto( $row ) ) { $id = $row[0]; }

  return $id;
}

$id = add_user( 'jerry', 'pass' );

var_dump( $id );
?>

Thay vì nhận được giá trị id tối đa, tôi bây giờ chỉ cần sử dụng câu lệnh INSERT để chèn dữ liệu, sau đó sử dụng một câu lệnh SELECT để lấy id của bản ghi vừa mới được chèn vào. Mã này đơn giản hơn nhiều và hiệu quả hơn so với phiên bản gốc và lược đồ liên quan của nó.

Một lựa chọn khác đối với chức năng tăng tự động của MySQL là sử dụng phương thức nextId() trong hệ thống PEAR DB. Trong trường hợp của MySQL, điều này tạo ra một bảng tuần tự mới và quản lý việc sử dụng một cơ chế khóa phức tạp. Lợi thế của việc sử dụng phương thức này là nó sẽ hoạt động trên các hệ thống cơ sở dữ liệu khác.

Dù bằng cách nào, bạn nên sử dụng một hệ thống quản lý sự tăng các ID duy nhất cho bạn và không dựa vào hệ thống mà bạn truy vấn đầu tiên, sau đó tăng giá trị của chính bạn và thêm bản ghi. Cách tiếp cận thứ hai dễ bị ảnh hưởng theo các điều kiện ganh đua trên các trang có khối lượng cao.


Vấn đề 3: Sử dụng nhiều cơ sở dữ liệu

Một khi chúng ta thấy ứng dụng mà mỗi bảng ở trong một cơ sở dữ liệu riêng biệt. Có nhiều lý do để thực hiện điều đó trong các cơ sở dữ liệu rất lớn, nhưng đối với ứng dụng trung bình, bạn không cần mức phân đoạn này. Ngoài ra, mặc dù có thể thực hiện các truy vấn liên quan trên nhiều cơ sở dữ liệu, tôi rất khuyên bạn chống lại nó. Cú pháp phức tạp hơn. Quản lý sao lưu và khôi phục lại không dễ dàng. Cú pháp có thể hoặc không thể làm việc giữa các máy cơ sở dữ liệu khác nhau. Và thật khó khăn để tiếp tục theo cấu trúc quan hệ khi các bảng được chia trên nhiều cơ sở dữ liệu.

Vì vậy, nhiều cơ sở dữ liệu sẽ giống thế nào? Để bắt đầu, bạn cần một số dữ liệu. Liệt kê 7 cho thấy dữ liệu này được chia thành bốn tệp.

Liệt kê 7. Các tệp cơ sở dữ liệu
Files.sql:
CREATE TABLE files (
  id MEDIUMINT,
  user_id MEDIUMINT,
  name TEXT,
  path TEXT
);

Load_files.sql:
INSERT INTO files VALUES ( 1, 1, 'test1.jpg', 'files/test1.jpg' );
INSERT INTO files VALUES ( 2, 1, 'test2.jpg', 'files/test2.jpg' );

Users.sql:
DROP TABLE IF EXISTS users;
CREATE TABLE users (
  id MEDIUMINT,
  login TEXT,
  password TEXT
);

Load_users.sql:
INSERT INTO users VALUES ( 1, 'jack', 'pass' );
INSERT INTO users VALUES ( 2, 'jon', 'pass' );

Trong phiên bản nhiều cơ sở dữ liệu của các tệp này, bạn sẽ nạp câu lệnh SQL vào trong một cơ sở dữ liệu, sau đó nạp các câu lệnh SQL users (những người sử dụng) vào cơ sở dữ liệu khác. Mã PHP để truy vấn cơ sở dữ liệu cho các tệp này liên kết với người dùng cụ thể được hiển thị dưới đây.

Liệt kê 8. Getfiles.php
<?php
require_once("DB.php");

function get_user( $name )
{
  $dsn = 'mysql://root:password@localhost/bad_multi1';
  $db =& DB::Connect( $dsn, array() );
  if (PEAR::isError($db)) { die($db->getMessage()); }

  $res = $db->query( "SELECT id FROM users WHERE login=?",
  array( $name ) );
  $uid = null;
  while( $res->fetchInto( $row ) ) { $uid = $row[0]; }

  return $uid;
}

function get_files( $name )
{
  $uid = get_user( $name );

  $rows = array();

  $dsn = 'mysql://root:password@localhost/bad_multi2';
  $db =& DB::Connect( $dsn, array() );
  if (PEAR::isError($db)) { die($db->getMessage()); }

  $res = $db->query( "SELECT * FROM files WHERE user_id=?",
  array( $uid ) );
  while( $res->fetchInto( $row ) ) { $rows[] = $row; }

  return $rows;
}

$files = get_files( 'jack' );

var_dump( $files );
?>

Hàm get_user kết nối tới cơ sở dữ liệu chứa bảng của những người sử dụng và lấy ra ID cho một người dùng đã biết. Hàm get_files kết nối đến bảng các tệp và lấy ra các hàng có kết hợp với người dùng đã biết.

Cách tốt hơn để làm tất cả những điều này là nạp dữ liệu vào một cơ sở dữ liệu, sau đó thực hiện một truy vấn, như được hiển thị bên dưới.

Liệt kê 9. Getfiles_good.php
<?php
require_once("DB.php");

function get_files( $name )
{
  $rows = array();

  $dsn = 'mysql://root:password@localhost/good_multi';
  $db =& DB::Connect( $dsn, array() );
  if (PEAR::isError($db)) { die($db->getMessage()); }

  $res = $db->query(
  "SELECT files.* FROM users, files WHERE
  users.login=? AND users.id=files.user_id",
  array( $name ) );
  while( $res->fetchInto( $row ) ) { $rows[] = $row; }

  return $rows;
}

$files = get_files( 'jack' );

var_dump( $files );
?>

Mã này không chỉ ngắn hơn mà nó cũng dễ hiểu hơn và hiệu quả hơn. Thay vì thực hiện hai truy vấn, chúng ta đang thực hiện một.

Trong khi vấn đề này lạc điệu, chúng ta đã thấy nó trong thực tế có đủ thời gian để biết rằng tất cả các bảng phải ở trong cùng một cơ sở dữ liệu, trừ khi có một lý do cấp thiết khác.


Vấn đề 4: Không sử dụng các mối quan hệ

Các cơ sở dữ liệu quan hệ không giống như các ngôn ngữ lập trình. Chúng không có kiểu mảng. Thay vào đó, chúng sử dụng các mối quan hệ giữa các bảng để tạo ra cấu trúc một-tới-nhiều giữa các đối tượng, chúng có cùng tác dụng như một mảng. Một vấn đề mà tôi đã thấy với các ứng dụng là khi các kỹ sư cố gắng sử dụng một cơ sở dữ liệu như thể nó đã là một ngôn ngữ lập trình, tạo arrays (các mảng) bằng cách sử dụng chuỗi văn bản với các trình nhận dạng tách nhau bằng dấu phẩy. Hãy xem lược đồ dưới đây.

Liệt kê 10. Bad.sql
DROP TABLE IF EXISTS files;
CREATE TABLE files (
  id MEDIUMINT,
  name TEXT,
  path TEXT
);

DROP TABLE IF EXISTS users;
CREATE TABLE users (
  id MEDIUMINT,
  login TEXT,
  password TEXT,
  files TEXT
);

INSERT INTO files VALUES ( 1, 'test1.jpg', 'media/test1.jpg' );
INSERT INTO files VALUES ( 2, 'test1.jpg', 'media/test1.jpg' );
INSERT INTO users VALUES ( 1, 'jack', 'pass', '1,2' );

Một người dùng trong hệ thống có thể có nhiều tệp. Trong một ngôn ngữ lập trình, bạn sẽ sử dụng một mảng để biểu diễn các tệp liên kết với người dùng. Trong ví dụ này, lập trình viên đã chọn để tạo ra một trường files (các tệp) chứa danh sách các id tệp được phân cách bằng dấu phẩy. Để có được một danh sách tất cả các tệp cho người dùng cụ thể, lập trình viên trước tiên phải đọc hàng từ bảng những người dùng, sau đó phân tích cú pháp văn bản của tệp và chạy một câu lệnh SELECT riêng lẻ cho mỗi tệp. Mã này được hiển thị bên dưới..

Liệt kê 11. Get.php
<?php
require_once("DB.php");

function get_files( $name )
{
  $dsn = 'mysql://root:password@localhost/bad_norel';
  $db =& DB::Connect( $dsn, array() );
  if (PEAR::isError($db)) { die($db->getMessage()); }

  $res = $db->query( "SELECT files FROM users WHERE login=?",
  array( $name ) );
  $files = null;
  while( $res->fetchInto( $row ) ) { $files = $row[0]; }

  $rows = array();

  foreach( split( ',',$files ) as $file )
  {
    $res = $db->query( "SELECT * FROM files WHERE id=?",
      array( $file ) );
    while( $res->fetchInto( $row ) ) { $rows[] = $row; }
  }

  return $rows;
}

$files = get_files( 'jack' );

var_dump( $files );
?>

Kỹ thuật này chậm, khó bảo trì và không tận dụng tốt cơ sở dữ liệu. Giải pháp duy nhất là tái kiến trúc lược đồ này để đưa nó trở lại thành một dạng quan hệ truyền thống, như được hiển thị dưới đây.

Liệt kê 12. Good.sql
DROP TABLE IF EXISTS files;
CREATE TABLE files (
  id MEDIUMINT,
  user_id MEDIUMINT,
  name TEXT,
  path TEXT
);

DROP TABLE IF EXISTS users;
CREATE TABLE users (
  id MEDIUMINT,
  login TEXT,
  password TEXT
);

INSERT INTO users VALUES ( 1, 'jack', 'pass' );
INSERT INTO files VALUES ( 1, 1, 'test1.jpg', 'media/test1.jpg' );
INSERT INTO files VALUES ( 2, 1, 'test1.jpg', 'media/test1.jpg' );

Ở đây, mỗi tệp có liên quan đến người dùng thông qua hàm user_id trong bảng tệp. Điều này hầu như có vẻ lạc hậu với bất kỳ ai nhìn thấy điều này như là một mảng. Chắc chắn, các mảng không tham chiếu đến đối tượng trong mảng -- thực tế, ngược lại hoàn toàn. Nhưng trong một cơ sở dữ liệu quan hệ, đây là cách những thứ này làm và lí do các truy vấn là nhanh hơn và dễ dàng hơn nhiều. Liệt kê 13 cho thấy mã PHP tương ứng.

Liệt kê 13. Get_good.php
<?php
require_once("DB.php");

function get_files( $name )
{
  $dsn = 'mysql://root:password@localhost/good_rel';
  $db =& DB::Connect( $dsn, array() );
  if (PEAR::isError($db)) { die($db->getMessage()); }

  $rows = array();
  $res = $db->query(
    "SELECT files.* FROM users,files WHERE users.login=?
      AND users.id=files.user_id",
        array( $name ) );
  while( $res->fetchInto( $row ) ) { $rows[] = $row; }

  return $rows;
}

$files = get_files( 'jack' );

var_dump( $files );
?>

Ở đây, chúng ta thực hiện một truy vấn đến cơ sở dữ liệu để nhận được tất cả các hàng. Mã này không phức tạp và nó sử dụng cơ sở dữ liệu như nó đã được dự định.


Vấn đề 5: Mẫu n+1

Tôi không thể nói cho bạn biết bao nhiêu lần chúng ta đã nhìn thấy các ứng dụng lớn trong đó mã này đầu tiên lấy ra một danh sách các thực thể -- gọi là các khách hàng -- sau đó quay trở lại và lấy ra chúng từng người một để có được những chi tiết cho từng thực thể. Chúng ta gọi nó là mẫu n+1 vì đó là có bao nhiêu truy vấn sẽ được thực hiện -- một truy vấn để lấy ra danh sách tất cả các thực thể, rồi một truy vấn cho mỗi một trong n thực thể. Đây không phải là một vấn đề khi n = 10, nhưng sẽ là gì khi n = 100 hoặc n = 1000? Rồi, sự thiếu khả năng thực sự đóng góp vào. Liệt kê 14 cho thấy một ví dụ về một lược đồ như vậy.

Liệt kê 14. Schema.sql
DROP TABLE IF EXISTS authors;
CREATE TABLE authors (
  id MEDIUMINT NOT NULL AUTO_INCREMENT,
  name TEXT NOT NULL,
  PRIMARY KEY ( id )
);

DROP TABLE IF EXISTS books;
CREATE TABLE books (
  id MEDIUMINT NOT NULL AUTO_INCREMENT,
  author_id MEDIUMINT NOT NULL,
  name TEXT NOT NULL,
  PRIMARY KEY ( id )
);

INSERT INTO authors VALUES ( null, 'Jack Herrington' );
INSERT INTO authors VALUES ( null, 'Dave Thomas' );

INSERT INTO books VALUES ( null, 1, 'Code Generation in Action' );
INSERT INTO books VALUES ( null, 1, 'Podcasting Hacks' );
INSERT INTO books VALUES ( null, 1, 'PHP Hacks' );
INSERT INTO books VALUES ( null, 2, 'Pragmatic Programmer' );
INSERT INTO books VALUES ( null, 2, 'Ruby on Rails' );
INSERT INTO books VALUES ( null, 2, 'Programming Ruby' );

Đây là lược đồ đáng tin cậy. Không có gì sai ở đây. Vấn đề là ở trong mã truy cập vào cơ sở dữ liệu để tìm tất cả các sách cho một tác giả cụ thể, như được hiển thị dưới đây.

Liệt kê 15. Get.php
<?php
require_once('DB.php');

$dsn = 'mysql://root:password@localhost/good_books';
$db =& DB::Connect( $dsn, array() );
if (PEAR::isError($db)) { die($db->getMessage()); }

function get_author_id( $name )
{
  global $db;

  $res = $db->query( "SELECT id FROM authors WHERE name=?",
    array( $name ) );
  $id = null;
  while( $res->fetchInto( $row ) ) { $id = $row[0]; }
  return $id;
}

function get_books( $id )
{
  global $db;

  $res = $db->query( "SELECT id FROM books WHERE author_id=?",
    array( $id ) );
  $ids = array();
  while( $res->fetchInto( $row ) ) { $ids []= $row[0]; }
  return $ids;
}

function get_book( $id )
{
  global $db;

  $res = $db->query( "SELECT * FROM books WHERE id=?", array( $id ) );
  while( $res->fetchInto( $row ) ) { return $row; }
  return null;
}

$author_id = get_author_id( 'Jack Herrington' );
$books = get_books( $author_id );
foreach( $books as $book_id ) {
  $book = get_book( $book_id );
  var_dump( $book );
}
?>

Nếu bạn nhìn vào mã ở dưới cùng, bạn có khả năng suy nghĩ đến chính mình, "Ôi, điều này thực sự sạch sẽ." Trước tiên, nhận được id, tác giả, rồi nhận được một danh sách các cuốn sách, sau đó nhận được thông tin về mỗi cuốn sách. Chắc chắn, nó sạch sẽ -- nhưng nó có hiệu quả không? Không. Hãy nhìn xem chúng ta phải thực hiện bao nhiêu các truy vấn để lấy ra chỉ các sách của Jack Herrington. Một truy vấn để nhận được một id, một truy vấn khác để nhận được một danh sách các cuốn sách, sau đó một truy vấn cho mỗi cuốn sách. Năm truy vấn cho ba cuốn sách!

Giải pháp này là có một hàm thực hiện một số lượng lớn truy vấn, như liệt kê dưới đây.

Liệt kê 16. Get_good.php
<?php
require_once('DB.php');

$dsn = 'mysql://root:password@localhost/good_books';
$db =& DB::Connect( $dsn, array() );
if (PEAR::isError($db)) { die($db->getMessage()); }

function get_books( $name )
{
  global $db;

  $res = $db->query(
    "SELECT books.* FROM authors,books WHERE
      books.author_id=authors.id AND authors.name=?",
      array( $name ) );
  $rows = array();
  while( $res->fetchInto( $row ) ) { $rows []= $row; }
  return $rows;
}

$books = get_books( 'Jack Herrington' );
var_dump( $books );
?>

Bây giờ lấy ra danh sách yêu cầu chỉ một truy vấn, nhanh. Nó có nghĩa là tôi có khả năng sẽ phải có một số các kiểu các phương thức này với các tham số khác nhau, nhưng thực sự không có sự lựa chọn nào. Nếu bạn muốn có một ứng dụng PHP có thể so sánh được với nhau, bạn phải sử dụng hiệu quả cơ sở dữ liệu và điều đó có nghĩa là các truy vấn thông minh hơn.

Vấn đề với ví dụ này là nó quá rõ ràng. Thông thường, các kiểu của vấn đề n+1 hay n*n nhạy cảm hơn nhiều. Và chúng chỉ xuất hiện khi người quản trị cơ sở dữ liệu chạy một trình định hình truy vấn trên hệ thống của bạn khi nó có vấn đề về hiệu năng.


Kết luận

Các cơ sở dữ liệu là các công cụ mạnh và -- như tất cả các công cụ mạnh mẽ -- chúng có thể bị lạm dụng nếu bạn không biết cách sử dụng chúng cho đúng. Thủ thuật phía sau việc xác định và giải quyết các vấn đề này là hiểu rõ hơn về công nghệ bên dưới. Quá lâu, tôi đã nghe các nhà mã hóa logic nghiệp vụ than thở rằng họ không muốn phải hiểu cơ sở dữ liệu hoặc mã SQL. Họ bao bọc cơ sở dữ liệu trong các đối tượng và tự hỏi tại sao hiệu năng lại kém đến như vậy.

Họ không nhận ra rằng sự hiểu biết SQL là nguyên tắc cơ bản để chuyển cơ sở dữ liệu từ một hoàn cảnh khó khăn thành một đồng minh mạnh. Nếu bạn sử dụng cơ sở dữ liệu hàng ngày, nhưng SQL không phù hợp với bạn, hãy đọc The Art of SQL (Nghệ thuật SQL). Đây là hướng dẫn thiết thực, được viết rõ ràng để nhận được nhiều nhất bên ngoài một cơ sở dữ liệu.

Tài nguyên

Học tập

  • The Art of SQL (Nghệ thuật SQL), của Stephane Faroult và Peter Robson, là một cuốn sách phải đọc đối với các lập trình viên, những người sử dụng cơ sở dữ liệu trong các ứng dụng của họ.
  • PHP.net là điểm khởi đầu cho tất cả mọi thứ PHP.
  • Tài liệu PEAR DB là một tài nguyên xuất sắc.
  • Tài liệu các chức năng PDO có thể đưa bạn lên đến tốc độ trên PHP Data Objects (PDO- Các đối tượng dữ liệu PHP).
  • MySQL.org có tài liệu hướng dẫn xuất sắc với các ví dụ hiển thị cách sử dụng một cơ sở dữ liệu tốt hơn.
  • Truy cập tài nguyên dự án PHP của developerWorks IBM để tìm hiểu thêm về PHP.
  • Theo sát với các sự kiện kỹ thuật và webcast của developerWorks.
  • Hãy xem các hội nghị, các cuộc triển lãm thương mại, webcast sắp tới và các sự kiện trên thế giới mà các nhà phát triển mã nguồn mở của IBM đang quan tâm.
  • Truy cập vào lĩnh vực mã nguồn mở của developerWorks với các thông tin hướng dẫn rộng lớn, các công cụ và dự án để giúp bạn phát triển với các công nghệ mã nguồn mở và sử dụng chúng với các sản phẩm của IBM.
  • Để nghe các cuộc phỏng vấn và thảo luận thú vị với các nhà phát triển phần mềm, hãy xem developerWorks podcasts.

Lấy sản phẩm và công nghệ

  • Đổi mới dự án phát triển nguồn mở tiếp theo của của bạn với phần mềm dùng thử IBM, có sẵn để tải xuống hoặc trên đĩa DVD.

Thảo luận

Bình luận

developerWorks: Đăng nhập

Các trường được đánh dấu hoa thị là bắt buộc (*).


Bạn cần một ID của IBM?
Bạn quên định danh?


Bạn quên mật khẩu?
Đổi mật khẩu

Bằng việc nhấn Gửi, bạn đã đồng ý với các điều khoản sử dụng developerWorks Điều khoản sử dụng.

 


Ở lần bạn đăng nhập đầu tiên vào trang developerWorks, một hồ sơ cá nhân của bạn được tạo ra. Thông tin trong bản hồ sơ này (tên bạn, nước/vùng lãnh thổ, và tên cơ quan) sẽ được trưng ra cho mọi người và sẽ đi cùng các nội dung mà bạn đăng, trừ khi bạn chọn việc ẩn tên cơ quan của bạn. Bạn có thể cập nhật tài khoản trên trang IBM bất cứ khi nào.

Thông tin gửi đi được đảm bảo an toàn.

Chọn tên hiển thị của bạn



Lần đầu tiên bạn đăng nhập vào trang developerWorks, một bản trích ngang được tạo ra cho bạn, bạn cần phải chọn một tên để hiển thị. Tên hiển thị của bạn sẽ đi kèm theo các nội dung mà bạn đăng tải trên developerWorks.

Tên hiển thị cần có từ 3 đến 30 ký tự. Tên xuất hiện của bạn phải là duy nhất trên trang Cộng đồng developerWorks và vì lí do an ninh nó không phải là địa chỉ email của bạn.

Các trường được đánh dấu hoa thị là bắt buộc (*).

(Tên hiển thị cần có từ 3 đến 30 ký tự)

Bằng việc nhấn Gửi, bạn đã đồng ý với các điều khoản sử dụng developerWorks Điều khoản sử dụng.

 


Thông tin gửi đi được đảm bảo an toàn.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=70
Zone=Nguồn mở
ArticleID=459877
ArticleTitle=Năm vấn đề chung về cơ sở dữ liệu trên PHP
publish-date=01082010