TÌM HIỂU VỀ NOSQL

1. NoSQL là gì ?

  • NoSQL còn có nghĩa là Non-Relational (NoRel) – không ràng buộc. Tuy nhiên, thuật ngữ đó ít phổ dụng hơn và ngày nay người ta thường dịch NoSQL thành Not Only SQL – Không chỉ SQL. NoSQL ám chỉ đến những cơ sở dữ liệu không dùng mô hình dữ liệu quan hệ để quản lý dữ liệu trong lĩnh vực phần mềm.

  • Thuật ngữ NoSQL được giới thiệu lần đầu vào năm 1998 sử dụng làm tên gọi chung cho các lightweight open source relational database (cơ sở dữ liệu quan hệ nguồn mở nhỏ) nhưng không sử dụng SQL cho truy vấn.

  • Vào năm 2009, Eric Evans, nhân viên của Rackspace giới thiệu lại thuật ngữ NoSQL trong một hội thảo về cơ sở dữ liệu nguồn mở phân tán. Thuật ngữ NoSQL đánh dấu bước phát triển của thế hệ database mới: distributed (phân tán) + non-relational (không ràng buộc).

  • Một số đặc điểm nhận dạng cho thế hệ database mới này bao gồm:

Schema-free Hỗ trợ mở rộng dễ dàng API đơn giản Eventual consistency (nhất quán cuối) và/hoặc transactions hạn chế trên các thành phần dữ liệu đơn lẻ Không giới hạn không gian dữ liệu

2. Một số thuật ngữ liên quan

  • Non-relational: relational – ràng buộc – thuật ngữ sử dụng đến các mối quan hệ giữa các bảng trong cơ sở dữ liệu quan hệ (RDBMs) sử dụng mô hình khóa gồm 2 loại khóa: khóa chính và khóa phụ (primary key + foreign key) để ràng buộc dữ liệu nhằm thể hiện tính nhất quán dữ liệu từ các bảng khác nhau. Non-relational là khái niệm không sử dụng các ràng buộc dữ liệu cho nhất quán dữ liệu ở NoSQL database.

  • Distributed storage: mô hình lưu trữ phân tán các file hoặc dữ liệu ra nhiều máy tính khác nhau trong mạng LAN hoặc Internet dưới sự kiểm soát của phần mềm.

  • Eventual consistency (nhất quán cuối): tính nhất quán của dữ liệu không cần phải đảm bảo ngay tức khắc sau mỗi phép write. Một hệ thống phân tán chấp nhận những ảnh hưởng theo phương thức lan truyền và sau một khoảng thời gian (không phải ngay tức khắc), thay đổi sẽ đi đến mọi điểm trong hệ thống, tức là cuối cùng (eventually) dữ liệu trên hệ thống sẽ trở lại trạng thái nhất quán.

  • Vertical scalable (khả năng mở rộng chiều dọc): Khi dữ liệu lớn về lượng, phương pháp tăng cường khả năng lưu trữ và xử lý bằng việc cải tiến phần mềm và cải thiện phần cứng trên một máy tính đơn lẻ được gọi là khả năng mở rộng chiều dọc. Ví dụ việc tăng cường CPUs, cải thiện đĩa cứng, bộ nhớ trong một máy tính,… cho DBMs nằm trong phạm trù này. Khả năng mở rộng chiều dọc còn có một thuật ngữ khác scale up.

  • Horizontal scalable (khả năng mở rộng chiều ngang): Khi dữ liệu lớn về lượng, phương pháp tăng cường khả năng lưu trữ và xử lý là dùng nhiều máy tính phân tán. Phân tán dữ liệu được hỗ trợ bởi phần mềm tức cơ sở dữ liệu.

  • Trong khi giá thành phần cứng ngày càng giảm, tốc độ xử lý, bộ nhớ ngày càng tăng thì horizontal scalable là một lựa chọn đúng đắn. Hàng trăm máy tính nhỏ được chập lại tạo thành một hệ thống tính toán mạnh hơn nhiều so với vi xử lý RISC truyền thống đơn lẻ. Mô hình này tiếp tục được hỗ trợ bởi các công nghệ kết nối Myrinet và InfiniBand. Từ đó chúng ta có thể quản lý, bảo trì từ xa, xây dựng batch procession (xử lý đồng loạt tập lệnh) tốt hơn. Do những đòi hỏi về tốc độ xử lý I/O cao, lượng cực lớn dữ liệu,… scale horizontally sẽ thúc đẩy các công nghệ lưu trữ mới phát triển giống như object storage devices (OSD).

3. Một số khái niệm mới trong NoSQL

  • Fields – tương đương với khái niệm Columns trong SQL

  • Document – thay thế khái niệm row trong SQL. Đây cũng chính là khái niệm làm nên sự khác biệt giữa NoSQL và SQL, 1 document chứa số cột (fields) không cố định trong khi 1 row thì số cột(columns) là định sẵn trước.

  • Collection – tương đương với khái niệm table trong SQL. Một collection là tập hợp các document. Điều đặc biệt là một collection có thể chứa các document hoàn toàn khác nhau.

  • Key-value – cặp từ khóa – giá trị được dùng để lưu trữ dữ liệu trong NoSQL

  • Cursor – tạm dịch là con trỏ. Chúng ta sẽ sử dụng cursor để lấy dữ liệu từ database.

  • Indexes ~ counterparts: Trong các hệ cơ sở dữ liệu quan hệ, các cột được định nghĩa theo bảng còn với hệ cơ sở dữ liệu không ràng buộc, các cột được định nghĩa ở mỗi document. Bởi thế, các document quản lí gần như tất cả, các collection không cần quản lí chặt chẽ những gì đang xảy ra trong nó nữa .

4. Ưu điểm của NoSQL

  • Các RDBMs hiện tại đã bộc lộ những yếu kém như việc đánh chỉ mục một lượng lớn dữ liệu, phân trang, hoặc phân phối luồng dữ liệu media (phim, ảnh, nhạc, …). Cơ sở dữ liệu quan hệ được thiết kế cho những mô hình dữ liệu nhỏ thường xuyên đọc viết trong khi các Social Network Services lại có một lượng dữ liệu cực lớn và cập nhật liên tục do số lượng người dùng quá nhiều ở một thời điểm. Thiết kế trên Distributed NoSQL giảm thiểu tối đa các phép tính toán, I/O liên quan kết hợp với batch processing đủ đảm bảo được yêu cầu xử lý dữ liệu của các mạng dịch vụ dữ liệu cộng đồng này. Facebook, Amazon là những ví dụ điểm hình.

  • Về cơ bản, các thiết kế của NoSQL lựa chọn mô hình lưu trữ tập dữ liệu theo cặp giá trị key-value. Khái niệm node được sử dụng trong quản lý dữ liệu phân tán. Với các hệ thống phân tán, việc lưu trữ có chấp nhận trùng lặp dữ liệu. Một request truy vấn tới data có thể gửi tới nhiều máy cùng lúc, khi một máy nào nó bị chết cũng không ảnh hưởng nhiều tới toàn bộ hệ thống. Để đảm bảo tính real time trong các hệ thống xử lý lượng lớn, thông thường người ta sẽ tách biệt database ra làm 2 hoặc nhiều database. Một database nhỏ đảm bảo vào ra liên tục, khi đạt tới ngưỡng thời gian hoặc dung lượng, database nhỏ sẽ được gộp (merge) vào database lớn có thiết kế tối ưu cho phép đọc (read operation). Mô hình đó cho phép tăng cường hiệu suất I/O – một trong những nguyên nhân chính khiến performance trở nên kém.

NoSQL có các đặc điểm sau:

  • High Scalability: Gần như không có một giới hạn cho dữ liệu và người dùng trên hệ thống.
  • High Availability: Do chấp nhận sự trùng lặp trong lưu trữ nên nếu một node (commodity machine) nào đó bị chết cũng không ảnh hưởng tới toàn bộ hệ thống.
  • Atomicity: Độc lập data state trong các operation.
  • Consistency: chấp nhận tính nhất quán yếu, cập nhật mới không đảm bảo rằng các truy xuất sau đó thấy ngay được sự thay đổi. Sau một khoảng thời gian lan truyền thì tính nhất quán cuối cùng của dữ liệu mới được đảm bảo.
  • Durability: dữ liệu có thể tồn tại trong bộ nhớ máy tính nhưng đồng thời cũng được lưu trữ lại đĩa cứng.
  • Deployment Flexibility: việc bổ sung thêm/loại bỏ các node, hệ thống sẽ tự động nhận biết để lưu trữ mà không cần phải can thiệp bằng tay. Hệ thống cũng không đòi hỏi cấu hình phần cứng mạnh, đồng nhất.
  • Modeling flexibility: Key-Value pairs, Hierarchical data (dữ liệu cấu trúc), Graphs.
  • Query Flexibility: Multi-Gets, Range queries (load một tập giá trị dựa vào một dãy các khóa).

5. Ví dụ về mô hình Wide Column Store / Column Families

  • Column family databases được biết đến nhiều nhất qua sự triển khai BigTable của Google. Nhìn bên ngoài vào nó giống với cơ sỡ dữ liệu quan hệ nhưng thực sự thì có sự khác biệt rất lớn từ bên trong. Một trong những khác biệt đó chính là việc lưu trữ dữ liệu theo dòng (trong cơ sở dữ liệu quan hệ) so với việc lưu trữ dữ liệu theo cột (trong column family databases). Nhưng sự khác biệt lớn là ở chính khái niệm của nó. Chúng ta không thể áp dụng cùng một giải pháp mà chúng ta sử dụng trong cơ sở dữ liệu quan hệ vào trong cơ sở dữ liệu column family. Đó là bởi vì cơ sở dữ liệu cột (column family database) phi quan hệ. Các khái niệm sau đây rất quan trọng để hiểu được column family database làm việc như thế nào: o Column family o Super column o Column
  • Column và super column trong column family database dùng thay thế nhau, có nghĩa là chúng sẽ là 0 byte nếu chúng không có chứa dữ liệu. Không giống như một bảng, thứ duy nhất chúng ta cần xác định trong column family database tên cột và các tùy chọn chính (không có lược đồ cố định).
  • Ý tưởng cơ bản: o Column families: Một column family là cách thức dữ liệu được lưu trữ trên đĩa. Tất cả dữ liệu trong một cột sẽ được lưu trên cùng một file. Một column family có thể chứa super column hoặc column. o Super column: Một super column có thể được dùng như một dictionary(kiểu từ điển). Nó là một column có thể chứa những column khác (mà không phải là super column). o Column: Một column là một bộ gồm tên, giá trị và dấu thời gian (thông thường chỉ quan tâm tới key-value).
  • Hiểu được lược đồ đượcc thiết kế như thế nào rất quan trọng. Nếu chúng ta thiết kế lược đồ không đúng, chúng ta không thể lấy được dữ liệu. Column family database cung cấp một trong hai cách truy vấn: key hoặc là key range. Điều này có nghĩa là, vì CFDB có thể phân tán nên khóa xác định vị trí vật lý thực sự mà dữ liệu được lưu trữ. Dữ liệu được lưu trữ dựa trên sự sắp xếp của column family và chúng ta không có cách nào để thay đổi sự sắp xếp này (ngoại trừ việc sắp xếp tăng dần hay giảm dần).
  • Không giống như trong cơ sở dữ liệu quan hệ, thứ tự sắp xếp không bị ảnh hưởng bởi giá trị cột, nhưng lại bị ảnh hưởng bởi tên cột.
  • Giả sử trong column family Users, một dòng với khóa chính là ”@ayende”, một cột name với giá trị “Ayende Rahien” và một cột location với giá trị “Israel”. CFDB sẽ sắp xếp vật lý trong User column family file như sau:
@ayende/location = "Israel"
@ayende/name = "Ayende Rahien"
  • Bởi vì location đứng trước name nên cột location sẽ được lưu trước cột name. Tương tự cho super column, ví dụ cột Friends, “@ayende” có 2 friend thì trong file Friends column family được lưu trữ vật lý như sau:
@ayende/friends/arava= 945
@ayende/friends/rose = 14
  • Điều này rất quan trọng để hiểu được cách làm việc của CFDB. Giả sử chúng ta có mô hình twitter cần lưu trữ: users và tweets. Chúng ta định nghĩa 3 cột: o User: sắp xếp theo UTF8 o Tweets: sắp xếp theo Sequential Guid o UsersTweets: super column, sắp xếp theo Sequential Guid
  • Giả sử chúng ta muốn tạo một User:
cfdb.Users.Insert(key: "@ayende", name: "Ayende Rahien", location: "Israel", profession: "Wizard");

Hình ảnh nội tuyến 1

  • Giờ chúng ta sẽ tạo một tweet:
var firstTweetKey = "Tweets/" + SequentialGuid.Create();
cfdb.Tweets.Insert(key: firstTweetKey, application: "TweekDeck", text: "Err, is this on?", private: true;
var secondTweetKey = "Tweets/" + SequentialGuid.Create();
cfdb.Tweets.Insert(key: secondTweetKey, app: "Twhirl", version: "1.2", text: "Well, I guess this is my mandatory hello world”, public: true, version: 1.2);

Hình ảnh nội tuyến 2

  • Một vài lưu ý là: o Thực sự giá trị của khóa không quan trọng, nhưng nó là những chuỗi liên tiếp cho phép chúng ta sắp xếp sau này, o Cả hai dòng chứa dữ liệu khác nhau bởi vì chúng ta không có lược đồ cố định. o Chúng ta không có cách nào liên kết giữa một user và tweet
  • Trong cơ sở dữ liệu quan hệ, chúng ta sẽ định nghĩa một cột là UserId trong Tweet cho phép chúng ta liên kết với bảng User. Hơn nữa, cơ sở dữ liệu quan hệ còn cho phép chúng ta truy vấn tweets theo UserId. CFDB không làm được điều này, chúng ta không thể truy vấn theo dữ liệu cột. Thay vì thế, điều duy nhất CFDB cho phép chúng ta là truy vấn theo khóa. Chúng ta định nghĩa một index thứ hai, nơi mà cột UsersTweets xuất hiện:
cfdb.UsersTweets.Insert(key: "@ayende", timeline: { SequentialGuid.Create(): firstTweetKey } );
cfdb.UsersTweets.Insert(key: "@ayende", timeline: { SequentialGuid.Create(): secondTweetKey } );
  • Chúng ta thêm dữ liệu vào cột UsersTweets một dòng với khóa là “@ayende”, super column timeline với 2 cột – tên mỗi cột là sequential guid cho phép sắp xếp.

Hình ảnh nội tuyến 3

  • Lưu ý: Câu hỏi đặt ra là chúng ta có thể tạo ra super column trong cột User để lưu giữ quan hệ. Câu trả lời là: chúng ta có thể làm điều đó, ngoại trừ một column family có thể chứa hoặc column hoặc super column, không thể chứa cả hai.
  • Để lấy những tweets của một user, chúng ta cần thực thi:
var tweetIds =cfdb.UsersTweets.Get("@ayende")
.FetchSuperColumnValues("timeline")
.Take(25)
.OrderByDescending()
.Select(x=>x.Value);
var tweets = cfdb.Tweets.Get(tweetIds);
  • Về cơ bản chúng ta thực hiện 2 truy vấn: o Truy vấn thứ nhất vào cột UsersTweets, yêu cầu cột và giá trị trong timeline super column với khóa là “@ayende” o Truy vấn thứ hai là truy vấn vào cột Tweets để lấy giá trị thực sự của Tweet.
  • Kiểu này chúng ta thường thấy trong NoSQL. Nó được gọi là ”secondary index”, một cách truy cập dữ liệu nhanh chóng theo khóa dựa trên một giá trị khác entity/row/document. Đây là một ví dụ về truy vấn Tweeys theo User trên dữ liệu chúng ta có. Nếu không tạo ra secondary index thì chúng ta không có cách nào trả lời cho câu hỏi: “cho xem 25 tweets mới nhất của ayende?”
  • Bởi vì dữ liệu được sắp xếp theo tên cột và giảm dần, chúng ta có thể lấy được 25 tweets mới nhất của ayende. Và sẽ như thế nào nếu ta muốn truy vấn 25 tweets mới nhất (không theo một user nào)? Rất đơn giản, chỉ cần truy vấn vào cột Tweets sắp xếp theo khóa giảm dần.

6. Các điểm hạn chế ?

CFDB viết tắt cho (mô hình Wide Column Store / Column Families).

  • CFDB khó khăn trong việc nghiên cứu lúc đầu ví nó nhìn bên ngoài thì giống cơ sở dữ liệu quan hệ mà lại bị hạn chế rất nhiều. Không có “join”, không có khả năng truy vấn thực sự(ngoại trừ truy vấn theo khóa chính), không có sự phong phú sự hỗ trợ như cơ sở dữ liệu quan hệ làm được. SQLite hay Access đem lại nhiều lợi ích hơn CFDB. Tại sao CFDB lại hạn chế như vậy?

  • Câu trả lời khá đơn giản. CFDB được thiết kế đê chạy trên một số lượng lớn các máy, và lưu trữ một lượng dữ liệu cực lớn. Chúng ta không thể lưu trữ một lượng lớn dữ liệu trong cơ sở dữ liệu quan hệthậm chí là nhiều máy như Oracle RAC sẽ nhanh chóng bị sụp đổ hoặc là chết rất nhanh về kích thước của dữ liệu và những truy vấn đó được CFDB xử lý một cách dễ dàng. Nhớ rằng CFDB loại bỏ các khái niệm trừu tượng, những thứ làm cho nó cứng nhắt khi chạy trên một cụm máy.

  • Lý do mà CFDB không hỗ trợ join là join yêu cầu chúng ta quét toàn bộ tập hợp dữ liệu. Điều đó yêu cầu phải có một nơi có cái nhìn tổng quát về toàn bộ cơ sở dữ liệu(kết quả trong nút cổ chai và điểm duy nhất bị thất bại) hoặc thực hiện một truy vấn thực sự trên tất cả các máy có trong cụm. CFDB không cung cấp cách thức để truy vấn theo cột hay giá trị bởi vì nó sẽ yêu cầu một index trên toàn bộ tập hợp dữ liệu (hay chỉ là một cột duy nhất). Và một lần nữa không thực tế, không thể chạy truy vấn trên tất cả các máy. Bằng cách giới hạn truy vấn theo khóa, CFDB đảm bảo rằng nó biết chính xác về node mà truy vấn có thể thực hiện trên đó. Có nghĩa là mỗi truy vấn được chạy trên một tập nhỏ dữ liệu là cho chi phí rẻ hơn nhiều.

  • Nhiều hạn chế, khó sử dụng nhưng CFDB lại có khả năng mở rộng cao. Đây là điều chúng ta cần quan tâm tới.