Chọn công cụ phù hợp cho công việc
Các lựa chọn và kết hợp chúng ta có sẵn trong việc chọn datastore chứng minh rằng chúng ta không còn ở trong thế giới datastore "one-size-fits-all" (một kích cỡ vừa cho tất cả).
Ngày nay, có những lý do thuyết phục để kết hợp SQL datastores (như MySQL, PostgreSQL, Oracle hoặc SQL Server) với NoSQL datastores (MongoDB, CouchDB, Neo4J và những loại khác). Mặc dù Oracle vẫn có thể là system of record (hệ thống ghi chép chính) ưa chuộng cho doanh nghiệp, nó không còn là lựa chọn duy nhất.
Các nhà phát triển đang bắt đầu sử dụng kết hợp SQL và NoSQL để giải quyết vấn đề của họ – đôi khi ngược ý愿 của DBA hoặc bộ phận IT.
Năm loại datastore rộng rãi
Có năm loại datastore rộng rãi trong thế giới ngày nay: column-family (cột-gia đình), document (tài liệu), graph (đồ thị), key-value (khóa-giá trị) và relational (quan hệ). Polyglot persistence (đa ngôn ngữ lưu trữ) nghĩa đen là dùng nhiều ngôn ngữ để lưu trữ hoặc persist dữ liệu. Về mặt thực tế, điều này có nghĩa là chúng ta có thể dùng Cypher, JSON, SQL hoặc nhiều ngôn ngữ truy vấn khác để truy cập dữ liệu từ cùng một ứng dụng. Việc sử dụng các datastore và ngôn ngữ khác nhau này đang trở nên nổi bật hơn khi các nhà phát triển tìm kiếm công cụ tốt hơn để "laser in" (tập trung chính xác) giải pháp cho nhu cầu persistence của họ.
Sadalage và Fowler ghi nhận sự cần thiết của polyglot persistence trong NoSQL Distilled:
Các database khác nhau được thiết kế để giải quyết các vấn đề khác nhau. Việc sử dụng một database engine duy nhất cho tất cả yêu cầu thường dẫn đến giải pháp không hiệu suất; lưu trữ dữ liệu giao dịch, cache thông tin session, duyệt đồ thị khách hàng và sản phẩm bạn bè họ mua là những vấn đề về cơ bản khác nhau.
Hãy nghĩ về mối quan hệ dữ liệu. Giải pháp RDBMS giỏi tại việc thực thi (enforcing) mối quan hệ tồn tại. Nếu chúng ta muốn khám phá mối quan hệ, hoặc phải tìm dữ liệu từ các bảng khác nhau thuộc về cùng một object, thì việc dùng RDBMS bắt đầu trở nên khó khăn.
Lựa chọn datastore归结为两个标准:
- Cấu trúc của dữ liệu được lưu trữ
- Các truy vấn được dùng để tương tác với dữ liệu
Cách chúng ta truy vấn dữ liệu thay đổi cách chúng ta cấu trúc nó. Như Sadalage và Fowler nói, relational datastores xuất sắc tại việc thực thi các entity liên quan; tuy nhiên, chúng cản trở ngay khi chúng ta cần khám phá các mối quan hệ khác giữa các entity đó.
Dưới đây, tôi thảo luận một use case (trường hợp sử dụng), lưu trữ dữ liệu CraigsList với MongoDB, và suy đoán về cách họ có thể thực hiện điều này.
Các "người chơi": MongoDB, MySQL và CraigsList
MongoDB
MongoDB đã trở thành một lựa chọn NoSQL ưa chuộng thay thế MySQL. Nhiều lợi ích bao gồm scalability (khả năng mở rộng), auto-sharding và sẵn sàng native bindings cho các ngôn ngữ lập trình phổ biến ngày nay. Khác biệt cốt lõi giữa MongoDB và relational datastores là cách MongoDB suy nghĩ và lưu trữ dữ liệu. Thay vì dùng tập hợp các bảng với ràng buộc khóa ngoại (foreign key constraints) để thực thi mối quan hệ, dữ liệu trong MongoDB được biểu diễn như một tập hợp các document (tài liệu).
Các document tương tự (không giống hệt) các hàng (rows) hoặc tuple trong cấu trúc dữ liệu quan hệ. Phân loại và thuật ngữ của document datastore đến trực tiếp từ việc dữ liệu được lưu trữ như các tài liệu JSON nhóm vào các collection (bộ sưu tập). Độ sâu của các document này là không giới hạn và hoàn toàn có thể kiểm tra (inspectable) với truy vấn hoặc index. Thường thì một biểu diễn dữ liệu tốt cho MongoDB có thể đạt được bằng cách denormalize (phi chuẩn hóa) dữ liệu phù hợp cho relational database. Tất nhiên, các truy vấn cụ thể bạn muốn thực hiện nên hướng dẫn quy trình này.
Thêm thông tin về sắc thái của MongoDB có thể tìm thấy trên website của MongoDB.
MySQL
Klassik mà mọi người biết và yêu thích, MySQL đã có mặt từ "thời kỳ đầu" (theo thang đo thời gian tính toán) và dễ dàng là DBMS được sử dụng rộng rãi nhất. Chức năng nó cung cấp đã cho phép ứng dụng mô hình hóa dữ liệu gần một thập kỷ và đóng vai trò system of record cho nhiều mục đích kinh doanh. Ngày nay khi người ta nghĩ đến relational database, họ có thể nghĩ đến MySQL.
MySQL cung cấp triển khai của mô hình dữ liệu quan hệ kinh điển. Sử dụng type theory (lý thuyết kiểu) và set theory (lý thuyết tập hợp), nó được phát triển những năm 1970 bởi E.F. Codd. Khả năng được chuẩn hóa (normalized), lập kế hoạch (planned) hoặc introspected (tự kiểm tra) theo chương trình, làm cho các hệ thống dữ liệu quan hệ cực kỳ phổ biến. Thực tế, các datastore này tiếp tục được ưa chuộng vì chúng giải quyết vấn đề mô hình hóa dữ liệu theo cách tổng quát.
CraigsList
Một doanh nghiệp trực tuyến nổi tiếng sử dụng cả MongoDB và MySQL datastore là CraigsList. Việc áp dụng song song hai datastore của họ được nêu trong một MongoDB Case Study, nhưng dưới đây là bản tóm tắt.
Do yêu cầu quy định, Craigslist phải giữ bản ghi kỹ thuật số của các classifieds (quảng cáo phân loại). Với hơn một triệu classifieds mới mỗi ngày, đó là lượng dữ liệu đáng kể để CraigsList giữ lại. Nó dùng MySQL datastore để giữ tất cả thông tin hoạt động (active) về classifieds, trong khi MongoDB dùng để lưu trữ dữ liệu lưu trữ (archived) – có thể là bất cứ thứ gì quá 30 ngày. Como một phần của thay đổi kinh doanh bình thường, schema dữ liệu của dữ liệu lưu trữ thay đổi. Bằng cách dùng MongoDB cho dữ liệu lưu trữ, CraigsList đã phân đoạn dữ liệu hiệu quả và giảm thiểu vấn đề do migration schema (di chuyển schema).
Comme một thí nghiệm tư duy (thought experiment), tôi muốn suy đoán về một triển khai có thể để dùng MongoDB và MySQL song song trong ứng dụng kiểu CraigsList. Rất không có khả năng đây là cách CraigsList thực sự lưu trữ dữ liệu, nhưng đây là một cách thú vị để xem nhiều datastore có thể làm việc cùng nhau như thế nào cho một trang web quen thuộc, giao dịch cao, high-transactional.
Làm thế nào để thực hiện? (How is it done?)
Các nhà phát triển và kỹ sư không thể tránh khỏi vấn đề khi họ thực hiện schema update trên một SQL database cồng kềnh. Điều này có thể tránh được bằng cách đơn giản là có ít dữ liệu hơn để "fix up" (sửa chữa) sau khi schema update được áp dụng. Nỗi đau của các migration hoặc schema update này thường tăng tỷ lệ thuận với lượng dữ liệu.
Trong ví dụ của chúng ta, hãy tưởng tượng CraigsList yêu cầu một thông tin mới từ người dùng bán một món hàng. Vì schema phải được cập nhật, CraigsList sẽ muốn giảm kích thước dữ liệu bị ảnh hưởng để giảm thiểu nỗi đau của việc cập nhật.
Sau một vài chu kỳ archive và migration này, CraigsList sẽ xây dựng một bộ sưu tập lớn dữ liệu heterogeneous (đa dạng) yêu cầu một datastore schemaless (không schema) nếu nó muốn nằm ở một vị trí đơn lẻ. MongoDB phù hợp rất tốt cho điều này.
Một ví dụ schema cho classifieds sẽ trông như sau (tái triển khai không ngại ngần từ craigslist-clone):
CREATE TABLE `classifieds` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(75) COLLATE utf8_unicode_ci DEFAULT NULL,
`description` text COLLATE utf8_unicode_ci,
`location` varchar(75) COLLATE utf8_unicode_ci DEFAULT NULL,
`adtype` varchar(1) COLLATE utf8_unicode_ci DEFAULT 'O',
`email` varchar(75) COLLATE utf8_unicode_ci DEFAULT NULL,
`phone` varchar(75) COLLATE utf8_unicode_ci DEFAULT NULL,
`activation_code` varchar(40) COLLATE utf8_unicode_ci DEFAULT NULL,
`status` tinyint(4) DEFAULT '0',
`category_id` int(11) DEFAULT NULL,
`subcategory_id` int(11) DEFAULT NULL,
`city_id` int(11) DEFAULT NULL,
`permalink` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`image_file_name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`image_content_type` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`image_file_size` int(11) DEFAULT NULL,
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
Tất nhiên, CraigsList rất có thể có schema khác và ít nhất đã khám phá schema hiện tại sau vài vòng lặp. Ngoài ra, họ có thể quyết định thay đổi cách tổ chức dữ liệu và thay đổi schema lại trong tương lai. Chúng ta muốn dùng các trường created_at và updated_at để quyết định khi nào archive dữ liệu trong MySQL.
Giả sử chính sách classifieds của Craigslist quy định classified sẽ có sẵn trên website trong hai tuần. Sau thời gian này, họ muốn classified vẫn có sẵn nhưng không nhất thiết active (trong MySQL). Để thực hiện điều này, chúng ta có thể dùng kết hợp SQLAlchemy và pymongo:
Đầu tiên, chúng ta cần lấy dữ liệu ra khỏi instance MySQL. Chúng ta sẽ dùng SQLAlchemy để thực hiện việc này và để nó introspect (tự kiểm tra) schema của chúng ta (làm cho mã này tái sử dụng hơn cho mục đích này).
import sqlalchemy.schema
m = sqlalchemy.schema.MetaData("mysql://root:I'm required why?@192.0.2.3/craigslist")
m.reflect()
print m.tables.keys()
Nếu bạn kết nối thành công đến database, bạn sẽ thấy keys (tên cột) in ra theo phong cách python chuẩn: [u'classifieds', u'cities', u'subcategories', u'categories']. Chúng ta vẫn cần lấy các mục dữ liệu riêng lẻ ra khỏi các bảng này. Không chỉ có thể thấy chúng, SQLAlchemy cũng cung cấp giao diện thanh lịch để làm cho việc này dễ dàng hơn nhiều.
Bây giờ chúng ta có định nghĩa bảng từ introspection. Đã đến lúc tạo object maps hoặc truy vấn các bảng đó để lấy các mục dữ liệu chúng chứa. Truy vấn dưới sẽ trích xuất classifieds từ datastore (các bảng khác để làm bài tập cho người đọc).
import sqlalchemy.sql
connection = m.bind.connect()
classifieds = m.tables['classifieds']
query = classifieds.select()
result = connection.execute(query)
for row in result:
print dict(row.items())
Đoạn mã này dùng kết nối MySQL để truy vấn tất cả classifieds. Nó có thể dễ dàng mở rộng để xử lý tất cả các bảng, denormalize dữ liệu để phù hợp hơn với document style của MongoDB. Nhưng cho mục đích demo này, chúng ta chỉ tập trung vào bảng classifieds. Tại thời điểm này, chúng ta đã chuyển đổi mọi mục riêng lẻ trong bảng classifieds thành dictionary, chính xác thứ chúng ta cần để chèn vào MongoDB qua pymongo.
Mẫu tiếp theo cho thấy cách kết nối và chèn dictionary vào pymongo:
import pymongo
client = pymongo.MongoClient('mongodb://192.0.2.2')
db = client['craigslist']
collection = db['classifieds']
collection.insert({'_id': 1})
Vấn đề duy nhất bây giờ là cách SQLAlchemy và MongoDB chỉ định IDs của họ. SQLAlchemy dùng key id trong khi MongoDB dùng key _id. Do đó, chúng ta cần dịch key đó (quá trình khá đơn giản): classified['_id'] = classified.pop('id').
Kết luận
Mặc dù SQL và NoSQL datastores thường được mô tả như một đề xuất all-or-nothing (tất cả hoặc không có gì), nhưng thực tế chúng có thể được dùng cùng nhau để giải quyết các vấn đề phức tạp. Qua ví dụ này, chúng ta thấy rất ít mã được yêu cầu cho một hệ thống sử dụng cả MongoDB và MySQL datastore. Thực tế, điều này có thể được điều khiển bởi cron thay vì daemonized.
Khó khăn trong việc sử dụng nhiều datastore không nhất thiết nằm ở việc phát triển mã dịch (translation code) hoặc mã migration, mà việc quản trị các hệ thống bổ sung làm tăng độ khó. Việc bảo trì một datastore đã đòi hỏi chuyên môn (DBA hoặc admin có kiến thức datastore), và nhu cầu chuyên môn này tăng khi bạn giới thiệu thêm datastore.
Doanh nghiệp phải quyết định xem việc chạy nhiều datastore có giá trị không. Có công nghệ sẽ giúp giảm thiểu những thách thức này.
Ngoài các công nghệ tự động hóa như Chef và Salt, thách thức này có thể được giảm thiểu bằng cách tận dụng các nhà cung cấp dịch vụ như ObjectRocket, dịch vụ MongoDB được quản lý bởi Rackspace. Dù có sự phức tạp tăng thêm, nếu một vấn đề có lợi từ việc dùng nhiều datastore, đừng để những giả định ngăn cản bạn khám phá những giải pháp đó.