SQL vs NoSQL: Đâu là lựa chọn phù hợp cho dự án của bạn?
Trước khi đi vào chủ đề chính của bài viết, hãy cùng điểm qua lại các điểm khác nhau giữa hai hệ cơ sở dữ liệu SQL và NoSQL. Dựa trên các điểm khác nhau dưới đây, ta sẽ cùng áp dụng chúng vào các trường hợp dự án cụ thể và quyết định lựa chọn tốt nhất Cơ sở dữ liệu SQL Chứa các dữ liệu liên ...
Trước khi đi vào chủ đề chính của bài viết, hãy cùng điểm qua lại các điểm khác nhau giữa hai hệ cơ sở dữ liệu SQL và NoSQL. Dựa trên các điểm khác nhau dưới đây, ta sẽ cùng áp dụng chúng vào các trường hợp dự án cụ thể và quyết định lựa chọn tốt nhất
Cơ sở dữ liệu SQL
- Chứa các dữ liệu liên quan đến nhau ở trong các bảng
- Cần có một schema để định nghĩa trước các bảng sẽ sử dụng
- Khuyến khích các kiểu chuẩn hóa để giảm thiểu sự dư thừa dữ liệu
- Hỗ trợ các câu lệnh giúp JOINS các bảng với nhau để lấy các dữ liệu liên quan đến nhau từ nhiều bảng cùng lúc trong một câu lệnh duy nhất
- Áp dụng các quy tắc bảo đảm toàn vẹn dữ liệu
- Cung cấp các Transactions để đảm bảo phải có ít nhất hai cập nhật hoặc nhiều hơn thành công hoặc thất bại cùng nhau
- Có thể mở rộng nhưng sẽ tốn khá nhiều công
- Sử dụng ngôn ngữ mang tính miêu tả rất mạnh để query dữ liệu
Cơ sở dữ liệu NoSQL
- Chứa các dữ liệu liên quan đến nhau dưới dạng tài liệu JSON với các cặp key - value
- Có thể chứa dữ liệu mà không cần chỉ định ra một schema cụ thể
- Phải thường xuyên chuyển các cấu trúc đang được chuẩn hóa thành bình thường để các thông tin về một đối tượng nào đó được chứa đầy đủ trong một tài liệu duy nhất
- Không cần thiết phải sử dụng các câu lệnh JOINS ( trong trường hợp đang thực thi các cấu trúc chưa được chuẩn hóa)
- Cho phép lưu bất kỳ loại dữ liệu nào tại bất cứ đâu, ở bất cứ thời điểm nào mà không cần xác thực
- Đảm bảo chỉ cập nhật một tài liệu lưu trữ duy nhất
- Cung cấp hiệu năng tuyệt vời và tính mở rộng vô cùng dễ dàng
- Sử dụng các object dữ liệu dạng JSON để query
- Là một công nghệ mới hơn và gây hứng thú hơn
Cơ sở dữ liệu SQL sẽ là lý tưởng cho các dự án có một requirement đã được xác định sẵn và cần có sự thống nhất chặt chẽ giữa các dữ liệu. Còn cơ sở dữ liệu NoSQL sẽ là lựa chọn tốt cho một requirement chưa được xác định hoàn toàn, tức là dữ liệu không quá liên quan, không quá cần sự thống nhất, các dữ liệu chưa được xác định hết và có thể sẽ phát triển ra thêm nhiều hơn, các dự án vô cùng coi trọng yếu tố tốc độ và tính mở rộng. Theo một cách hiểu đơn giản hơn:
- SQL là Digital (Kĩ thuật số). Cơ sở dữ liệu này sẽ hoạt động tốt nhất với các đối tượng rời rạc, đã được định nghĩa rõ ràng với một yêu cầu kĩ thuật được xác định chính xác. Các trường hợp sử dụng thường thấy là: Các cửa hàng Online và Các hệ thống Ngân Hàng.
- NoSQL là Analog (Tương tự). Nó sẽ phù hợp nhất với các dữ liệu có tổ chức với một requirement mềm lỏng - có thể được thay đổi, mở rộng cho phù hợp mọi hoàn cảnh. Các trường hợp hay được sử dụng phải kể đến: Các mạng xã hội, Các hệ thống quản lý khách hàng và phân tích trang web.
Sẽ có rất ít các project phù hợp 100% với các đặc điểm kể trên của hai cơ sở dữ liệu này. Cả hai lựa chọn đều có thể khả thi nếu bạn có bộ dữ liệu bớt phức tạp hơn hoặc không được chuẩn hóa một cách tự nhiên. Những hãy chú ý đến các trường hợp ví dụ đã được đơn giản hóa và có sự bao quát rộng dưới dây. Các bạn biết rõ nhất về dự án của bản thân hơn ai khác, và mình cũng không khuyên rằng phải chuyển từ SQL sang NoSQL hay ngược lại trừ khi nó giúp dự án có được thêm các lợi ích đáng kể. Tất cả là sự lựa chọn của bạn. Hãy xem xét hết các điểm mạnh và điểm yếu từ khi mới bắt đầu project và bạn sẽ có được quyết định chính xác.
Hãy cùng tìm cách chế tạo lại bánh xe và sử dụng hệ cơ sở dữ liệu SQL để xây dựng hệ thống danh bạ. Và bảng contact - liên hệ là bảng đầu tiên ta nghĩ đến được định nghĩa với các trường sau đây:
- id
- title
- firstname
- lastname
- gender
- telephone
- address1
- address2
- address3
- city
- region
- zipcode
- country
Vấn đề thứ 1: Rất ít người chỉ có duy nhất một số điện thoại. Ta sẽ cần ít nhất ba trường cho điện thoại bàn, điện thoại di động và điện thoại nơi làm việc, nhưng bất kể khi ta thêm bao trường cho số điện thoại thì bất kỳ ai đó cũng có thể sẽ muốn có thêm nữa. Vậy ta đành thêm một bảng telephone để các contact có thể có bao nhiêu số điện thoại cũng được. Điều này cũng giúp chuẩn hóa dữ liệu, ta sẽ không phải cần có giá trị NULL cho các contact không có số liên lạc:
- contact_id
- number
- telphone_type ( có thể lưu dạng text hoặc để enum cho điện thoại bàn, di động v.v...)
Vấn đề thứ 2: Ta cũng sẽ gặp phải vấn đề tương tự với địa chỉ email và thêm lần nữa ta phải tạo ra bảng mới là bảng email:
- contact_id
- address
- email_type ( có thể lưu dạng text hoặc để enum cho email nhà, email công ty v.v...)
Vấn đề thứ 3: Người dùng có thể không muốn nhập vào một địa chỉ duy nhất, hoặc họ muốn nhập nhiều địa chỉ cho cùng một nơi làm việc, hay nhà v.v... Và vì thế ta lại cần thêm một bảng address:
- contact_id
- address_type ( có thể lưu dạng text hoặc để enum cho nhà, công ty v.v...)
- address1
- address2
- address3
- city
- region
- zipcode
- country
Và cuối cùng thì bảng contact ban đầu được giảm xuống còn:
- id
- title
- firstname
- lastname
- gender
Thật là tuyệt khi ta đã chuẩn hóa được cơ sở dữ liệu để có thể lưu bao nhiêu số điện thoại, email hay địa chỉ cũng được cho bất cứ contact nào. Nhưng thật không may khi...
Schema quá chặt chẽ và cứng nhắc
Ta cũng chưa xem xét hết các trường có thể có của một contact như middlename, date_of_birth , company hay job_role. Bất kể bao nhiêu trường chúng ta thêm vào thì cũng sẽ sớm nhận được các yêu cầu cập nhật thêm các trường khác như notes, relationship_status, social_network_account, hobby v.v... Sẽ là bất khả thi để có thể thấy trước được tất cả các lựa chọn, và chả nhẽ ta lại phải tạo một bảng otherdata với các cặp key - value để chứa các yêu cầu đó.
Dữ liệu bị phân mảnh
Thật là không dễ dàng gì để các developer hoặc các quản trị viên hệ thống có thể xem được hết cơ sở dữ liệu. Sự logic của chương trình cũng sẽ trở nên chậm hơn rất nhiều và phức tạp hơn nhiều, bởi vì sẽ không thực tiễn khi bạn muốn lấy ra dữ liệu của một contact trong một câu SELECT với hàng loạt các mệnh đề JOIN. ( Tất nhiên là bạn có thể, nhưng kết quả sẽ bao gồm tất cả sự kết hợp của các số điện thoại, email và địa chỉ. Nếu ai đó có đến 3 số điện thoại, 5 email và 2 địa chỉ, thì câu query SQL sẽ tạo ra đến 30 kết quả).
Cuối cùng, để có thế sử dụng full-text search cũng khá khó. Nếu ai đó nhập vào chuỗi bất kỳ nào vào, ta sẽ phải kiểm tra tất cả 4 bảng để xem nó là giá trị của bảng contact, telephone, email hay address và sắp xếp thứ tự các kết quả tương ứng. Và nếu bạn đã từng sử dụng search của WordPress, bạn sẽ thấy điều này khó chịu đến thế nào.
Giải pháp thay thế: NoSQL
Dữ liệu của các contact liên quan đến con người. Đó là các dữ liệu không thể đoán trước và có các yêu cầu khác nhau tại các thời điểm khác nhau. Việc xây dựng danh bạ sẽ được hưởng lợi từ việc sử dụng cơ sở dữ liệu NoSQL, có thể lưu trữ tất cả dữ liệu của một cá nhân vào một tài liệu lưu trữ duy nhất trong tập contacts:
{ name: [ "Nguyen", "Van", "A" ], company: "Công ty", jobtitle: "Tên công việc", telephone: { home: "0123456789", mobile: "9876543210", work: "2244668800" }, email: { personal: "van.a@gmail.com", work: "van.a@emailcongty.com" }, address: { home: { line1: "Gầm cầu", city: "Thành Phố", country: "Nước" } }, birthdate: ISODate("1980-01-01T00:00:00.000Z"), facebook: 'nguyenvana', note: "Chăm chỉ", weight: "69kg", photo: "52e86ad749e0b817d25c8892.jpg" }
Ở ví dụ này, ta vẫn chưa lưu trường gender, date_of_birth v.v... và cũng thêm vào các trường mà không cần thiết cho những người khác. Nhưng không có vấn đề gì bởi cơ sở dữ liệu NoSQL rất linh hoạt trong việc này, ta có thể thêm hoặc xóa các trường tùy ý.
Bởi vì dữ liệu của các contact được lưu trong một tài liệu duy nhất, ta có thể lấy một phần hoặc toàn bộ dữ liệu sử dụng 1 câu query duy nhất. Sử dụng full-text search cũng trở nên dễ dàng hơn; trong MongoDB ta có thể định nghĩa index cho tất cả các trường của contact sử dụng lệnh:
db.contact.createIndex({ "$**": "text" });
và sau đó thực hiện một câu full-text search sử dụng:
db.contact.find({ $text: { $search: "cái gì đó" } });
Một mạng xã hội có thể sử dụng cách lưu trữ các contact tương tự như trên, nhưng nó sẽ mở rộng về các tập hợp tính năng với các lựa chọn như các quan hệ, các trạng thái được cập nhật, tin nhắn và "likes". Các tính năng đó có thể sẽ được xây dựng hay loại bỏ tùy thuộc vào phản ứng của yêu cầu người dùng và sẽ rất khó để có thể đoán được dự án này sẽ phát triển như thế nào.
Thêm vào đó:
- Hầu hết các cập nhật về dữ liệu đều có một xuất phát điểm duy nhất: người dùng. Chẳng phải lúc nào hay tự dưng ta cần cập nhật hai hay nhiều trường cùng một lúc, nên việc có thêm tính năng như transaction là không cần thiết.
- Bất kể một số người dùng có thể nghĩ gì, thì một cập nhật trạng thái thất bại sẽ là một vấn đề rất nghiêm trọng có thể sẽ gây ra sự "nóng chảy toàn cầu" hay "thất thoát tài chính". Vậy nên giao diện và hiệu năng của ứng dụng chiếm tỉ trọng cao hơn so với tính thống nhất chặt chẽ của dữ liệu.
NoSQL xem ra là một lựa chọn vừa vặn và hợp lý. Cơ sở dữ liệu này sẽ cho phép ta xây dựng các tính năng lưu trữ các loại dữ liệu khác nhau một cách nhanh chóng. Ví dụ, tất cả các cập nhật trạng thái theo ngày của người dùng có thể được lưu trong một tài liệu duy nhất trong tập dữ liệu status
{ user_id: ObjectID("65f82bda42e7b8c76f5c1969"), update: [ { date: ISODate("2015-09-18T10:02:47.620Z"), text: "Hôm nay vui qá ahihi" }, { date: ISODate("2015-09-17T13:14:20.789Z"), text: "Hôm nay buồn thiu" } { date: ISODate("2015-09-17T12:33:02.132Z"), text: "Buồn vì tiền" } ] }
Ngay cả khi tài liệu này có thể trở nên dài hơn, ta vẫn có thể lấy một phần nhỏ của mảng, như các cập nhật gần đây nhất. Toàn bộ lịch sử trạng thái của tất cả người dùng cũng có thể được tìm một cách nhanh chóng.
Giờ hãy giả sử ta muốn lựa chọn lại 1 emoticon mới khi post một trạng thái. Đây sẽ là việc phải thêm một thư viện đồ họa vào trong mảng update. Không như lưu trữ bằng SQL, việc phải set emoticon của các tin nhắn về NULL là không cần thiết - chương trình của ta có thể show một ảnh mặc định hoặc không show nếu một emoticon không được set.
Hãy xem xét một hệ thống theo dõi hàng hóa của một nhà kho. Ta cần phải ghi lại những điều sau:
- Các sản phẩm được đưa đến kho và được đặt ở địa điểm hay khoang nào
- Sự dịch chuyển của các hàng hóa trong kho. VD: sắp xếp kho để các sản phẩm cùng loại được đặt cùng một vị trí
- Các đơn đặt hàng và việc di chuyển các hàng hóa khỏi nhà kho cho việc giao hàng
Yêu cầu về dữ liệu:
- Các thông tin về sản phẩm như số lượng hộp, kích thước và màu sắc có thể được lưu, nhưng đó là các dữ liệu rời rạc ta có thể nhận ra và ứng dụng cho bất cứ thứ gì. Ta không cần phải quan tâm đến các chi tiết cụ thể như tốc độ của vi xử lý máy tính hoặc ước tính tuổi thọ pin của một smartphone nào đó.
- Việc giảm thiểu tối đa sai sót phải được làm chặt chẽ. Ta không thể để hàng hóa biến mất hay được di chuyển đến vị trí nơi một sản phẩm khác đã và đang được để. 3.Trong trường hợp đơn giản nhất là ta phải ghi chép lại việc vận chuyển của các hàng hóa từ một địa điểm vật lý này đến chỗ khác - hay bỏ nó từ địa điểm A và đặt vào địa điểm B. Đó sẽ phải là 2 lần cập nhật cho cùng một hành động.
Ta cần có một phương thức lưu trữ chặt chẽ với sự tăng cường tính thống nhất giữa các dữ liệu cùng với sự hỗ trợ của transaction. Và hiện tại chỉ có hệ cơ sử dữ liệu SQL mới có thể đáp ứng được các yêu cầu trên.
Trên đây là một số trường hợp tiêu biểu nhất trong cuộc sống giúp bạn có thể tìm ra điểm tương đồng cho dự án của bạn. Nhưng mọi dự án đều khác nhau và tất nhiên bạn sẽ phải tự quyết định điều tốt nhất cho dự án của mình. Hãy cố gắng tiếp xúc với càng nhiều công nghệ càng tốt, càng nhiều kiến thức bạn càng có thể đưa ra được các lý do chính đáng để tự lựa chọn trong việc phải sử dụng SQL hay NoSQL. Chúc may mắn.