Top những lỗi lập trình PHP mắc phải khi làm việc với Mysql
Bạn có dám chắc là không mắc các lỗi lập trình PHP liệt kê ở đây không? Hãy cùng đọc và nhìn lại dự án của mình nhé.
Nội dung chính
Lỗi lập trình PHP không sử dụng MyISAM, InnoDB đúng lúc
MySQL có nhiều engine CSDL, nhưng thường chúng ta hay dùng MyISAM và InnoDB. Từ phiên bản Mysql 5.5 trở đi, thì InnoDB là engine mặc định được sử dụng. Vì đa phần ngày nay, các mã nguồn mở như diễn đàn, quản trị nội dung viết bằng PHP thường sử dụng InnoDB, tuy nhiên, do MyISAM không hỗ trợ các ràng buộc khóa ngoại và các nghiệp vụ (transaction), nên tốc độ nhanh hơn là InnoDB, vì vậy, không phải là chọn MyISAM hay InnoDB cho toàn bộ các bảng dữ liệu là sự lựa chọn hoàn toàn tốt.
Làm sao cho hiệu quả?
Bạn cần xác định rõ, bảng nào có dùng ràng buộc dữ liệu hay nghiệp vụ transaction thì phải chọn InnoDB, bảng nào chỉ đọc là chính thì nên dùng MyISAM.
Không kiểm tra dữ liệu đầu vào
Có một điều quan trọng cần ghi nhớ đó là “đừng bao giờ tin tưởng vào những gì người sử dụng nhập”. Cần kiểm tra tất cả các chuỗi được nhập vào ở phía PHP server – không dựa trên JavaScript. Các cuộc tấn công SQL injection đơn giản nhất phụ thuộc vào mã ví dụ như:
$username = $_POST["name"]; $password = $_POST["password"]; $sql = "SELECT userid FROM usertable WHERE username='$username' AND password='$password';"; // thực thi truy vấn...
Nó có thể bị bẻ khóa khi nhập vào “admin’; –” ở ô username. Chuỗi SQL lúc này sẽ tương đương với:
SELECT userid FROM usertable WHERE username='admin';
Người tấn công có thể login như “admin”; mà không cần phải biết mật khẩu.
Làm sao tránh?
Hãy sử dụng:
- mysql_real_escape_string() cho biến kiểu chuỗi.
- intval() cho biến integer.
- filter_var(). Đây là một hàm rất mạnh được sử dụng nhiều trong những năm gần đây, với các tính năng: xác định tính hợp lệ của dữ liệu, lọc các ký tự không mong muốn. Xem các loại filter tại đây.
Ví dụ cụ thể:
$validEmail = "contact@fullstackstation.com"; echo filter_var($validEmail, FILTER_VALIDATE_EMAIL); //contact@fullstackstation.com $invalidEmail = "contact@.com"; var_dump(filter_var($invalidEmail, FILTER_VALIDATE_EMAIL)); //bool(false)
Bạn thấy ví dụ trên, rất đơn giản để xác định được email có hợp lệ hay không.
Ngoài ra, còn có thêm FILTER_CALLBACK rất hữu dụng cho bạn khi cần lọc các dữ liệu mà PHP không cung cấp bộ lọc phù hợp.
function trimString($value) { return trim($value); } $username = filter_var(' you need to trim me ', FILTER_CALLBACK, array('options' => 'trimString')); echo $username; //you need to trim me
Không sử dụng UTF-8 đúng chuẩn
Xu hướng toàn cầu hóa khá mạnh mẽ, khiến cho các trang web cần có ít nhất 2 ngôn ngữ: tiếng Anh và ngôn ngữ địa phương như tiếng Việt.
Tất nhiên, sẽ thừa để nói ở đây là nên sử dụng UTF-8! Vì chắc chắc một điều là nếu bạn muốn lập trình website có ngôn ngữ tiếng Việt thì bắt buộc character-set phải là UTF-8 rồi. Nhưng có một điều mà bạn sẽ cần cân nhắc đó là chọn collation nào?
utf8_general_ci, utf8_unicode_ci hay là utf8_bin?
Theo các tài liệu nghiên cứu, thì utf8_general_ci chạy nhanh hơn utf8_unicode_ci, nhưng utf8_unicode_ci phổ (hỗ trợ) nhiều ngôn ngữ hơn. Nếu bạn bắt đầu làm quen với một công ty mới, thiết kế website cho khách hàng ở một quốc gia sử dụng tiếng mà bạn không biết nên dùng collation nào, lời khuyên là hãy dùng utf8_unicode_ci. Chậm mà chắc!
Đối với dự án nào yêu cầu độ tìm kiếm chính xác, hãy sử dụng utf8_bin, vì nó so sánh phân biệt HOA-thường (case-sensitive) còn 2 anh bạn ở trên là case-insensitive. Hơn nữa, nó so sánh mức binary, nên độ chính xác cao hơn là văn bản thường.
Thường ưu tiên hàm PHP hơn hàm SQL
Khi người dùng mới làm quen với MySQL, thì thường xu hướng để giải quyết vấn đề là sử dụng ngôn ngữ mà họ biết. Điều đó có thể dẫn tới những đoạn code không cần thiết và chậm hơn. Ví dụ, thay vì sử dụng phương thức avg() của MySQL thì người dùng lại sử dụng vòng lặp trong PHP để tính giá trị trung bình của tất cả các giá trị trong một tập kết quả trả về. Nhìn chung, nên sử dụng những thế mạnh của CSDL khi phân tích dữ liệu sẽ tạo hiệu quả cao hơn.
Đây là một số hàm, từ khóa hữu ích của Mysql, bạn nên nghiên cứu trước khi dùng bằng PHP:
- UNION: bạn thường viết cả đống code PHP để lấy dữ liệu thay vì dùng UNION.
- COUNT: nhớ count 1 tên cột (field) thôi, đừng COUNT(*)
- MAX, MIN: đừng có lấy cả đống dữ liệu về chỉ để tính min, max
- AVG: đã nói ở trên
- RAND: ORDER BY RAND() chẳng hạn, rất hữu dụng đó chứ!
- CONCAT: kết dữ liệu ngay trên kết quả từ Mysql
Trên đây là một vài kỹ thuật mà nếu mới viết PHP sử dụng Mysql cũng nên học trước, nếu bạn sử dụng hàm php quá nhiều sẽ dẫn đến lỗi lập trình php nhiều hơn và cũng khó phân tách giữa cơ sở dữ liệu và lập trình.
Không tối ưu hóa các truy vấn
99% các vấn đề về hiệu suất trong PHP đều bị gây ra bởi CSDL và các truy vấn tệ có thể làm chậm đi rất nhiều ứng dụng web của chúng ta. MySQL’s EXPLAIN statement, Query Profiler và nhiều công cụ khác có thể giúp chúng ta tìm ra những câu truy vấn không cần thiết.
Thông thường, nếu sử dụng PHP Framework, do phải thiết kế để đáp ứng rất nhiều nhu cầu của rất nhiều dự án khác nhau, nên các framework này không được tối ưu ở phần truy vấn, hoặc không được bật lên ngay từ đầu (bạn cần xem các phần nâng cao của các framework mới thấy). Do vậy, mỗi khi bắt đầu nghiên cứu PHP Framework mới, bạn cần chú ý nhất đến phần Model cơ bản và cả nâng cao. Hơn hết, nhớ khẩu quyết: “Fat Models Skinny Controllers” trong mô hình MVC nhé.
Sử dụng sai kiểu dữ liệu
MySQL cung cấp nhiều kiểu dữ liệu số, chuỗi, thời gian. Nếu chúng ta muốn lưu trữ ngày tháng thì sử dụng kiểu DATE hoặc DATETIME. Nếu sử dụng kiểu INTEGER hoặc STRING sẽ làm cho câu lệnh truy vấn trở nên phức tạp và đôi khi không thể sử dụng được. Hoặc khi bạn lưu địa chỉ IP, có thể thể dùng IP2LONG sau đó lưu vào dữ liệu, cách này tuy tiết kiệm được không gian lưu trữ, nhưng khó quản lý về mặt tìm kiếm.
Người dùng có thể tự định nghĩa kiểu dữ liệu của riêng mình, ví dụ lưu trữ tuần tự (serialized) các đối tượng, mảng PHP trong một chuỗi, để quản lý CSDL dễ dàng hơn, cách này có ưu điểm là có thể mở rộng, lưu trữ được nhiều dữ liệu chỉ trong 1 trường (field) duy nhất, nhưng điểm yếu của nó là khó tìm kiếm. Bạn thường phải sử dụng kiểu dữ liệu TEXT cho dữ liệu tự định nghĩa này, nếu dùng varchar thì ứng dụng bạn sẽ bùm vào một ngày không xa.
Cách sử dụng kiểu dữ liệu varchar cũng cần lưu ý. Nhiều bạn khai báo: username varchar(255) chẳng hạn, mặc dù tuy không sai nhưng cần nhớ thiết lập kiểm soát (validation) rõ ràng. Hoặc trong trường hợp là dùng OAuth, lưu mấy token của mấy anh Facebook, Twitter thì mỗi anh mỗi kiểu, ngắn dài khác nhau, cũng phải đọc tài liệu về định hướng mở rộng tương lai, có thể độ dài chuỗi sẽ khác đó nha. Không riêng gì lỗi lập trình PHP, nếu bạn sử dụng các ngôn ngữ lập trình khác mà không sử đúng loại dữ liệu thì chứng tỏ thiết kế chương trình cũng chưa tốt.
Sử dụng * trong câu truy vấn SELECT, COUNT
Điều tối kỵ là sử dụng * để trả về tất cả các cột trong bảng, hoặc sử dụng tất cả các field chỉ để COUNT. Chúng ta chỉ nên trích xuất dữ liệu mà chúng ta cần.
Cái vụ này thì nói mãi, mà vẫn cứ đầy rẫy lập trình viên dùng đó, đừng có khinh thường nó. Nhưng lỗi lập trình php này thì thường xảy ra đối với các bạn lập trình php hơn vì đa phần là thường mới vào nghề, kinh nghiệm chưa có nhiều nên không để ý những vấn đề này.
Thiếu hoặc thừa chỉ mục
Như một quy luật chung, chỉ mục chỉ được áp dụng cho những cột có tên trong mệnh đề WHERE của câu lệnh truy vấn SELECT. Ví dụ, giả sử chúng ta có một user table với một numeric ID (là khóa chính) và một địa chỉ email. Trong quá trình đăng nhập vào, MySQL phải xác định vị trí đúng của ID bằng cách tìm kiếm một địa chỉ email. Với một chỉ mục, MySQL có thể sử dụng thuật toán tìm kiếm nhanh để xác định vị trí của email gần như ngay lập tức. Nếu không có chỉ mục, MySQL phải kiểm tra từng mẩu tin cho tới khi tìm thấy địa chỉ mail. Thêm chỉ mục là cần thiết, tuy nhiên, chúng lại được tạo lại mỗi khi bảng INSERT hay UPDATE. Điều đó có thể ảnh hưởng đến hiệu suất. Vì vậy, chỉ nên thêm chỉ mục khi nào mà trường đó được sử dụng để so sánh một cách nhiều lần: ví dụ như đăng nhập username/password, hoặc các title của bài viết , còn content thì phải FULL-INDEX (cho mục đích tìm kiếm).
Quên sao lưu dự phòng dữ liệu
Hiếm khi CSDL bị hỏng nhưng cũng phải đề phòng trường hợp này có thể xảy ra. Các ổ đĩa cứng, server hoặc web host có thể ngừng hoạt động. Mất CSDL MySQL của ứng dụng là việc rất nghiêm trọng. Để tránh điều này xảy ra, chúng ta phải đảm bảo thường xuyên sao chép dữ liệu tự động hoặc thủ công.
Mục này nói là lỗi lập trình PHP không thì không đúng, tất cả các ngôn ngữ lập trình khác đều cần sao lưu dữ liệu thường xuyên để tránh điều đáng tiếc xảy ra.
Chưa biết Percona hay MariaDB?
Percona Server: https://www.percona.com/software/mysql-database/percona-server
MariaDB: https://mariadb.org/
Rất nhiều lập trình viên đã chuyển qua sử dụng MariaDB (hoàn toàn tương thích với Mysql), lý do chính là do Oracle đã mua lại Mysql và các bản cập nhật ít hơn, cũng như không mở thảo luận các vấn đề này với cộng đồng. Nói tóm lại, cộng đồng lập trình sử dụng Mysql nhiều là do tính mở của nó! Nếu bạn vẫn còn sử dụng Mysql gốc, đây vẫn được coi là lỗi lập trình php!
Chưa biết NoSQL là gì?
Ah uh, chưa biết Nosql cũng là rào cản của bạn trong xu hướng lập trình hiện nay. NoSQL không giúp cho bạn lập trình tốt hơn, cũng không phải là lỗi lập trình php, nó cũng không có transaction phức tạp, nhưng với NoSQL bạn có thể nghĩ đến việc mô hình dữ liệu động, uyển chuyển không còn bị ràng buộc trong n field nhất định nữa. Nếu bạn thực sự chưa biết Mysql và Nosql khác biệt như thế nào, hãy tham khảo bảng so sánh này giữa Mysql và Mongo DB (một đại diện của NoSQL).
Tham khảo: http://www.sitepoint.com/mysql-mistakes-php-developers/ (bài viết này có từ 5 năm trước, nhưng giá trị của nó thì vẫn còn nguyên cho các bạn mới bước chân vào giới lập trình PHP)