Nhớ lại hồi sinh viên khi mới học lập trình, cái mình ghét nhất là làm việc với cơ sở dữ liệu (Database), một đống thứ nhàm chán lặp đi lặp lại. Ngồi làm mấy bài tập về quản lý sinh viên, quản lý thư viện sách mà nghĩ sau này chắc tránh nó ra, chỉ làm những gì không cần đến CSDL thôi =))

Rồi dần dần học thêm nhiều, đọc thêm nhiều thì mới nhận ra với công nghệ hiện nay thì CSDL có thể coi là 1 phần tất yếu của phần mềm, không những thế nó còn là cả 1 mảng kiến thức lớn mà bắt buộc các lập trình viên cần nắm được.

Đến khi mình được làm quen với Android và được biết đến SQLite, lúc đó cái nhìn của mình về CSDL lại nhẹ nhàng đi 1 chút vì thấy nó khá là đơn giản và không có cảm giác phức tạp như Oracle hay SQL server từng được học.

Vậy SQLite là gì? Nó có gì hay ho?

SQLite là một hệ quản trị csdl quan hệ nhỏ gọn và không cần cài đặt, được tích hợp sẵn trên linux, window, android, ios,… và tích hợp trên nhiều phần mềm độc lập khác như trình duyệt web.

SQLite có nhiều ưu điểm như:

  • Nhẹ, nhẹ cả về bộ connector, nhẹ về dung lượng, và cũng không cần tiến trình hay service để hoạt động nó.
  • Không cài đặt client-server database. Đây là ưu điểm khá hay thích hợp cho các phần mềm nhỏ gọn, có thể triển khai trên nhiều máy mà không cần database server
  • Không user, phân quyền
  • Transaction tuân theo tiêu chuẩn ACID
  • Hỗ trợ hầu hết các tính năng của ngôn ngữ truy vấn
  • Dữ liệu được lưu trong 1 file data, có thể copy và backup dễ dàng

Chính vì quá nhỏ gọn do cắt giảm hết những gì phức tạp nên SQLite cũng có những nhược điểm không tránh khỏi:

  • Bảo mật không cao vì không có user cũng không có phân quyền, chỉ cần data file là lộ hết dữ liệu
  • SQLite sử dụng cơ chế khóa coarse-gained locking có thể hỗ trợ nhiều luồng đọc dữ liệu, nhưng chỉ có 1 luồng có thể ghi dữ liệu một lúc (Xem demo ở cuối bài).

Vì vậy SQLite chỉ phù hợp với các hệ thống nhỏ, sản phẩm cá nhân hoặc làm nền tảng cho người mới làm quen CSDL

Hướng dẫn kết nối tới 1 data file SQLite có sẵn
PHP. Thông thường như xampp hay wampserver đều đã tích hợp sẵn extensions PDO_SQLITE nên ta chỉ việc sử dụng mà không cần add thêm gì

<?php
$databaseFile = "demo.sqlite";
if (file_exists($databaseFile)) {
    $db = new PDO("sqlite:$databaseFile");
  $count = $db->query("SELECT date('now') as sysdate")->fetch(PDO::FETCH_ASSOC);
  var_dump($count);
}

Kết quả có được sau khi chạy trên trình duyệt

array (size=1)
  'sysdate' => string '2018-12-11' (length=10)

Java, với java thì bạn cần phải tải gói jar JDBC của SQLite cho Java tại địa chỉ https://bitbucket.org/xerial/sqlite-jdbc/downloads/, sau đó add gói jar vào project để sử dụng

package net.tunghuynh.sqlite;

import org.apache.log4j.Logger;
import java.sql.*;

public class Main {
    Logger logger = Logger.getLogger(getClass());
    public void getSysdate(){
        String databaseFile = "demo.sqlite";
        Connection conn = null;
        try {
            String url = "jdbc:sqlite:" + databaseFile;
            conn = DriverManager.getConnection(url);
            String sql = "select date('now') as sysdate";
            Statement statement = conn.createStatement();
            ResultSet rs = statement.executeQuery(sql);
            if (rs.next()) {
                System.out.println("Today: " + rs.getString("sysdate"));
            }
        } catch (SQLException e) {
            logger.error(e.getMessage(), e);
        } finally {
            try {
                if (conn != null) {
                    conn.close();
                }
            } catch (SQLException e) {
                logger.error(e.getMessage(), e);
            }
        }
    }
    public static void main(String[] args) {
        new Main().getSysdate();
    }
}

Kết quả sau khi thực thi chương trình

Today: 2018-12-11

Rất đơn giản và nhanh chóng, ta đã có một kết nối SQL hoàn chỉnh để sử dụng

Trường hợp bạn chưa có database file thì chương trình sẽ tự động tạo ra 1 file rỗng để sử dụng, bạn hoàn toàn có thể thực thi các câu lệnh CREATE TABLE ... để tạo bảng và sử dụng. Hoặc sử dụng các tool SQL để connect tới database file và tạo bảng theo ý muốn

Như đã nói ở trên, SQLite có nhược điểm lớn là chỉ cho phép 1 luồng ghi dữ liệu 1 lúc, giờ ta sẽ đi xác minh vấn đề này.

Trước tiên thử chạy nhiều luồng select dữ liệu cùng lúc

package net.tunghuynh.sqlite;

import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;

import java.sql.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class TestMulti {
    static {
        PropertyConfigurator.configure("etc/log4j.properties");
    }
    public static void main(String[] args) {
        ThreadPoolExecutor executorService = (ThreadPoolExecutor) Executors.newFixedThreadPool(10);
        for (int i = 0; i < 10; i++) {
            executorService.execute(new ConnectionRunnable());
        }
        shutdownAndAwaitTermination(executorService);
    }

    static void shutdownAndAwaitTermination(ExecutorService pool) {
        pool.shutdown();
        try {
            if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
                pool.shutdownNow();
                if (!pool.awaitTermination(60, TimeUnit.SECONDS))
                    System.err.println("Pool did not terminate");
            }
        } catch (Exception e) {
        }
    }
}

class ConnectionRunnable implements Runnable {
    Logger logger = Logger.getLogger(TestMulti.class);
    @Override
    public void run() {
        String databaseFile = "demo.sqlite";
        Connection conn = null;
        try {
            String url = "jdbc:sqlite:" + databaseFile;
            conn = DriverManager.getConnection(url);
            String sql = "select total from visited";
            Statement statement = conn.createStatement();
            for (int j = 0; j < 100000; j++) {
                statement.executeQuery(sql);
            }
            logger.info("Done");
        } catch (SQLException e) {
            System.err.println(e.getMessage());
        } finally {
            try {
                if (conn != null) {
                    conn.close();
                }
            } catch (SQLException e) {
                logger.error(e.getMessage(), e);
            }
        }
    }
}

Kết quả chương trình chạy bình thường không có lỗi xảy ra

2018-12-11 15:09:43 INFO  TestMulti:51 - Done
2018-12-11 15:09:43 INFO  TestMulti:51 - Done
2018-12-11 15:09:43 INFO  TestMulti:51 - Done
2018-12-11 15:09:43 INFO  TestMulti:51 - Done
2018-12-11 15:09:43 INFO  TestMulti:51 - Done
2018-12-11 15:09:43 INFO  TestMulti:51 - Done
2018-12-11 15:09:43 INFO  TestMulti:51 - Done
2018-12-11 15:09:44 INFO  TestMulti:51 - Done
2018-12-11 15:09:44 INFO  TestMulti:51 - Done
2018-12-11 15:09:44 INFO  TestMulti:51 - Done

Giờ thử với nhiều luồng update dữ liệu cùng lúc bằng cách sửa câu lệnh truy vấn

            String sql = "update visited set total=total+1";
            PreparedStatement statement = conn.prepareStatement(sql);
            for (int j = 0; j < 100000; j++) {
                statement.execute();
            }
            logger.info("Done");

Kết quả chương trình không thể thực hiện và báo database đã bị lock

2018-12-11 15:14:23 ERROR TestMulti:58 - [SQLITE_BUSY]  The database file is locked (database is locked)
2018-12-11 15:14:23 ERROR TestMulti:58 - [SQLITE_BUSY]  The database file is locked (database is locked)
2018-12-11 15:14:23 ERROR TestMulti:58 - [SQLITE_BUSY]  The database file is locked (database is locked)
2018-12-11 15:14:23 ERROR TestMulti:58 - [SQLITE_BUSY]  The database file is locked (database is locked)
2018-12-11 15:14:23 ERROR TestMulti:58 - [SQLITE_BUSY]  The database file is locked (database is locked)
2018-12-11 15:14:23 ERROR TestMulti:58 - [SQLITE_BUSY]  The database file is locked (database is locked)
2018-12-11 15:14:23 ERROR TestMulti:58 - [SQLITE_BUSY]  The database file is locked (database is locked)
2018-12-11 15:14:24 ERROR TestMulti:58 - [SQLITE_BUSY]  The database file is locked (database is locked)
2018-12-11 15:14:26 ERROR TestMulti:58 - [SQLITE_BUSY]  The database file is locked (database is locked)

Như vậy, qua 2 ví dụ đã thể hiện rõ nhược điểm của SQLite, nhưng với các ứng dụng hay hệ thống nhỏ không cần nhiều người dùng hay nhiều luồng xử lý ghi dữ thì đây không phải là vấn đề

Chúc các bạn ứng dụng được nhiều trong công việc và học tập 🙂

Bình luận