Lưu trữ JSON trong cơ sở dữ liệu so với việc có một cột mới cho mỗi khóa


211

Tôi đang triển khai mô hình sau để lưu trữ dữ liệu liên quan đến người dùng trong bảng của mình - Tôi có 2 cột - uid(khóa chính) và một metacột lưu trữ dữ liệu khác về người dùng ở định dạng JSON.

 uid   | meta
--------------------------------------------------
 1     | {name:['foo'], 
       |  emailid:['foo@bar.com','bar@foo.com']}
--------------------------------------------------
 2     | {name:['sann'], 
       |  emailid:['sann@bar.com','sann@foo.com']}
--------------------------------------------------

Đây có phải là một cách tốt hơn (hiệu suất-khôn ngoan, thiết kế-khôn ngoan) so với mô hình một-cột-per-bất động sản, nơi bảng sẽ có nhiều cột như uid, name, emailid.

Những gì tôi thích về mô hình đầu tiên là, bạn có thể thêm càng nhiều trường càng tốt, không có giới hạn.

Ngoài ra, tôi đã tự hỏi, bây giờ tôi đã thực hiện mô hình đầu tiên. Làm cách nào để thực hiện truy vấn trên đó, như, tôi muốn tìm nạp tất cả người dùng có tên như 'foo'?

Câu hỏi - Cách nào tốt hơn để lưu trữ dữ liệu liên quan đến người dùng (lưu ý rằng số lượng trường không cố định) trong cơ sở dữ liệu bằng cách sử dụng - JSON hoặc cột trên mỗi trường? Ngoài ra, nếu mô hình đầu tiên được thực hiện, làm thế nào để truy vấn cơ sở dữ liệu như được mô tả ở trên? Tôi có nên sử dụng cả hai mô hình, bằng cách lưu trữ tất cả dữ liệu có thể được tìm kiếm bởi một truy vấn trong một hàng riêng biệt và dữ liệu khác trong JSON (là một hàng khác nhau)?


Cập nhật

Vì sẽ không có quá nhiều cột mà tôi cần thực hiện tìm kiếm, nên sử dụng cả hai mô hình có khôn ngoan không? Khóa trên mỗi cột cho dữ liệu tôi cần tìm kiếm và JSON cho người khác (trong cùng một cơ sở dữ liệu MySQL)?


40
câu hỏi tuyệt vời! Nhưng tại sao bạn không chấp nhận một câu trả lời? điều đó sẽ giúp những người dùng khác (như tôi)
Sahar Ch.

Câu trả lời:


197

Cập nhật ngày 4 tháng 6 năm 2017

Cho rằng câu hỏi / câu trả lời này đã trở nên phổ biến, tôi cho rằng nó đáng để cập nhật.

Khi câu hỏi này ban đầu được đăng, MySQL không có hỗ trợ cho các loại dữ liệu JSON và sự hỗ trợ trong PostgreQuery đang ở giai đoạn sơ khai. Kể từ 5.7, MySQL hiện hỗ trợ loại dữ liệu JSON (ở định dạng lưu trữ nhị phân) và JSONB PostgreQuery đã trưởng thành đáng kể. Cả hai sản phẩm đều cung cấp các loại JSON hiệu suất có thể lưu trữ các tài liệu tùy ý, bao gồm hỗ trợ lập chỉ mục các khóa cụ thể của đối tượng JSON.

Tuy nhiên, tôi vẫn đứng trước tuyên bố ban đầu của mình rằng tùy chọn mặc định của bạn, khi sử dụng cơ sở dữ liệu quan hệ, vẫn phải là cột trên mỗi giá trị. Cơ sở dữ liệu quan hệ vẫn được xây dựng dựa trên giả định rằng dữ liệu trong đó sẽ được chuẩn hóa khá tốt. Trình hoạch định truy vấn có thông tin tối ưu hóa tốt hơn khi xem các cột so với khi xem các khóa trong tài liệu JSON. Khóa ngoại có thể được tạo giữa các cột (nhưng không phải giữa các khóa trong tài liệu JSON). Điều quan trọng: nếu phần lớn lược đồ của bạn đủ biến động để biện minh cho việc sử dụng JSON, thì ít nhất bạn có thể muốn xem xét liệu một cơ sở dữ liệu quan hệ có phải là lựa chọn đúng đắn hay không.

Điều đó nói rằng, rất ít ứng dụng có quan hệ hoàn hảo hoặc định hướng tài liệu. Hầu hết các ứng dụng có một số kết hợp của cả hai. Dưới đây là một số ví dụ mà cá nhân tôi đã thấy JSON hữu ích trong cơ sở dữ liệu quan hệ:

  • Khi lưu trữ địa chỉ email và số điện thoại cho một liên hệ, trong đó việc lưu trữ chúng dưới dạng giá trị trong một mảng JSON dễ quản lý hơn nhiều bảng riêng biệt

  • Lưu tùy chọn người dùng khóa / giá trị tùy ý (trong đó giá trị có thể là boolean, văn bản hoặc số và bạn không muốn có các cột riêng biệt cho các loại dữ liệu khác nhau)

  • Lưu trữ dữ liệu cấu hình không có lược đồ được xác định (nếu bạn đang xây dựng Zapier hoặc IFTTT và cần lưu trữ dữ liệu cấu hình cho mỗi tích hợp)

Tôi chắc chắn cũng có những người khác, nhưng đây chỉ là một vài ví dụ nhanh.

Câu trả lời gốc

Nếu bạn thực sự muốn có thể thêm nhiều trường như bạn muốn mà không giới hạn (ngoài giới hạn kích thước tài liệu tùy ý), hãy xem xét một giải pháp NoQuery như MongoDB.

Đối với cơ sở dữ liệu quan hệ: sử dụng một cột cho mỗi giá trị. Đặt một blob JSON vào một cột làm cho nó hầu như không thể truy vấn (và rất chậm khi bạn thực sự tìm thấy một truy vấn hoạt động).

Cơ sở dữ liệu quan hệ tận dụng các kiểu dữ liệu khi lập chỉ mục và được dự định thực hiện với cấu trúc chuẩn hóa .

Như một lưu ý phụ: điều này không có nghĩa là bạn không bao giờ nên lưu trữ JSON trong cơ sở dữ liệu quan hệ. Nếu bạn đang thêm siêu dữ liệu thực sự hoặc nếu JSON của bạn đang mô tả thông tin không cần truy vấn và chỉ được sử dụng để hiển thị, thì có thể là quá mức cần thiết để tạo một cột riêng cho tất cả các điểm dữ liệu.


1
Vì sẽ không có quá nhiều cột mà tôi cần thực hiện tìm kiếm, nên sử dụng cả hai mô hình có khôn ngoan không? Khóa trên mỗi cột cho dữ liệu tôi cần tìm kiếm và JSON cho người khác (trong cùng một cơ sở dữ liệu MySQL)?
ShuklaSannidhya

3
@Sann Bạn nên sử dụng một cột cho mỗi giá trị cho dữ liệu mà bạn muốn đọc hoặc truy vấn thường xuyên. Đặt tên của ai đó trong JSON không có ý nghĩa bởi vì, mặc dù bạn không có khả năng truy vấn dựa trên tên đó, nhưng bạn có thể sẽ cần nó rất thường xuyên. Đó là rất nhiều giải mã lãng phí về phía ứng dụng của bạn. Trừ khi bạn thực sự cảm thấy dữ liệu của mình được thể hiện tốt hơn dưới dạng JSON (và tin tôi đi, có lẽ là không), bạn không nên dùng đến điều đó.
Colin M

5
" virtually impossible to query" - hôm nay psql cho phép bạn tìm kiếm và lập chỉ mục jsonb của nó
ted

1
@ted đúng. Tuy nhiên, tại thời điểm viết câu trả lời này không thực sự có sẵn. Ngoài ra, câu hỏi này tham khảo MySQL trong đó khả năng là không có.
Colin M

3
@ColinM, vâng, tôi nhận ra nhận xét của tôi nhỏ hơn bài đăng của bạn 3 tuổi. Lý do tôi rời bỏ nó là vì nó có thể hữu ích và thay đổi quyết định cho người khác. Đối với tài liệu tham khảo về MySQL: có thể đúng, nhưng có "For relational databases"trong câu trả lời của bạn = P
ted

68

Giống như hầu hết mọi thứ "nó phụ thuộc". Không phải đúng hay sai / tốt hay xấu trong việc lưu trữ dữ liệu trong các cột hoặc JSON. Nó phụ thuộc vào những gì bạn cần làm với nó sau này. Cách dự đoán của bạn để truy cập dữ liệu này là gì? Bạn sẽ cần phải tham khảo chéo dữ liệu khác?

Những người khác đã trả lời khá tốt về sự đánh đổi kỹ thuật là gì.

Không nhiều người đã thảo luận rằng ứng dụng và tính năng của bạn phát triển theo thời gian và quyết định lưu trữ dữ liệu này ảnh hưởng đến nhóm của bạn như thế nào.

Vì một trong những cám dỗ của việc sử dụng JSON là tránh di chuyển lược đồ và do đó, nếu nhóm không bị kỷ luật, rất dễ dàng để gắn một cặp khóa / giá trị khác vào trường JSON. Không có di chuyển cho nó, không ai nhớ nó để làm gì. Không có xác nhận về nó.

Nhóm của tôi đã sử dụng JSON dọc theo các cột truyền thống trong postgres và lúc đầu, đó là điều tốt nhất kể từ khi cắt lát bánh mì. JSON hấp dẫn và mạnh mẽ, cho đến một ngày chúng tôi nhận ra rằng tính linh hoạt phải trả giá và nó đột nhiên là một điểm đau thực sự. Đôi khi, điểm đó tăng lên rất nhanh và sau đó khó thay đổi vì chúng tôi đã xây dựng rất nhiều thứ khác trên quyết định thiết kế này.

Làm thêm giờ, thêm các tính năng mới, có dữ liệu trong JSON dẫn đến các truy vấn tìm kiếm phức tạp hơn so với những gì có thể đã được thêm vào nếu chúng ta mắc kẹt vào các cột truyền thống. Vì vậy, sau đó chúng tôi bắt đầu câu cá các giá trị chính nhất định trở lại vào các cột để chúng tôi có thể tham gia và so sánh giữa các giá trị. Ý kiến ​​tồi. Bây giờ chúng tôi đã có sự trùng lặp. Một nhà phát triển mới sẽ tham gia và bị nhầm lẫn? Giá trị nào tôi nên lưu lại? JSON một hay cột?

Các trường JSON đã trở thành ngăn kéo rác cho những mảnh nhỏ này và kia. Không xác thực dữ liệu ở cấp cơ sở dữ liệu, không có tính nhất quán hoặc toàn vẹn giữa các tài liệu. Điều đó đã đẩy tất cả trách nhiệm đó vào ứng dụng thay vì kiểm tra loại cứng và ràng buộc từ các cột truyền thống.

Nhìn lại, JSON cho phép chúng tôi lặp đi lặp lại rất nhanh và lấy thứ gì đó ra khỏi cửa. Thật tuyệt vời Tuy nhiên, sau khi chúng tôi đạt được quy mô nhóm nhất định, tính linh hoạt cũng cho phép chúng tôi treo mình bằng một chuỗi nợ kỹ thuật dài, sau đó làm chậm tiến trình phát triển tính năng tiếp theo. Sử dụng cẩn thận.

Hãy suy nghĩ lâu dài và chăm chỉ về bản chất của dữ liệu của bạn. Đó là nền tảng của ứng dụng của bạn. Làm thế nào dữ liệu sẽ được sử dụng theo thời gian. Và làm thế nào nó có khả năng THAY ĐỔI?


6
"đó là sự linh hoạt cũng cho phép chúng tôi treo mình bằng một sợi nợ kỹ thuật dài" ẩn dụ rất hay!
Antoine Gallix

Sau nhiều năm phát triển và làm việc với những người khác nhau, nếu tôi nên viết về chủ đề này tôi sẽ viết điều tương tự. Hiện tại có rất nhiều nhà phát triển, trong đó nhiều người trong số họ thậm chí với nhiều năm kinh nghiệm họ không thực sự tăng cấp. Chúng tôi phải giữ mọi thứ đơn giản và đối với tôi, 2 điều mà chúng tôi luôn phải cân nhắc có thể "khung" thành công là khả năng mở rộng và khả năng duy trì của mã.
JohnnyJaxs

27

Chỉ cần ném nó ra khỏi đó, nhưng WordPress có cấu trúc cho loại công cụ này (ít nhất WordPress là nơi đầu tiên tôi quan sát thấy, nó có lẽ có nguồn gốc từ nơi khác).

Nó cho phép các khóa không giới hạn và tìm kiếm nhanh hơn so với sử dụng blob JSON, nhưng không nhanh như một số giải pháp NoQuery.

uid   |   meta_key    |   meta_val
----------------------------------
1         name            Frank
1         age             12
2         name            Jeremiah
3         fav_food        pizza
.................

BIÊN TẬP

Để lưu trữ lịch sử / nhiều khóa

uid   | meta_id    |   meta_key    |   meta_val
----------------------------------------------------
1        1             name            Frank
1        2             name            John
1        3             age             12
2        4             name            Jeremiah
3        5             fav_food        pizza
.................

và truy vấn thông qua một cái gì đó như thế này:

select meta_val from `table` where meta_key = 'name' and uid = 1 order by meta_id desc

1
Tôi tò mò muốn xem liệu một giải pháp NoQuery có thực sự hoạt động tốt hơn một truy vấn quan hệ trên khóa chỉ mục đúng không. Tôi nghi ngờ nó nên ít nhiều giống nhau trên một ví dụ cấp 1 như thế này.
Bruno

+1. Tôi cũng nhận thấy điều đó! Nhưng nó cung cấp cho bạn một bảng lớn (về các hàng). Ngoài ra, bạn không thể lưu trữ nhiều giá trị, giả sử, nếu người dùng thay đổi tên của họ, nhưng tôi cũng muốn giữ nguyên tên cũ, trong trường hợp đó tôi sẽ cần mô hình dữ liệu kiểu JSON.
ShuklaSannidhya

@Sann, nếu bạn muốn giữ giá trị cũ trong JSON, bạn cũng phải đổi tên khóa: bạn có thể thực hiện với EAV (đây là ví dụ này) hoặc JSON. Nó không đặc biệt khác nhau.
Bruno

Nó cung cấp cho bạn một bảng rất lớn, nhưng đối với các giá trị trùng lặp, bạn gặp phải vấn đề tương tự với JSON - bạn không thể có các khóa trùng lặp ở cùng cấp (ví dụ: hai khóa "tên") và mong đợi hành vi có thể dự đoán được.
Adam

Chắc chắn bạn không thể có các khóa trùng lặp, nhưng có thể có một mảng được liên kết với khóa đó. Kiểm tra emailidchìa khóa trong ví dụ tôi đã đưa ra trong câu hỏi của mình.
ShuklaSannidhya

13

nhược điểm của phương pháp này chính xác là những gì bạn đã đề cập:

nó làm cho RẤT chậm tìm thấy mọi thứ, vì mỗi lần bạn cần thực hiện tìm kiếm văn bản trên đó.

giá trị trên mỗi cột thay vì khớp với toàn bộ chuỗi.

Cách tiếp cận của bạn (dữ liệu dựa trên JSON) phù hợp với dữ liệu bạn không cần tìm kiếm và chỉ cần hiển thị cùng với dữ liệu thông thường của bạn.

Chỉnh sửa: Chỉ cần làm rõ, ở trên dành cho cơ sở dữ liệu quan hệ cổ điển. NoQuery sử dụng JSON trong nội bộ và có lẽ là một lựa chọn tốt hơn nếu đó là hành vi mong muốn.


1
Vì vậy, ý bạn là, tôi nên sử dụng cả hai. Khóa trên mỗi cột cho dữ liệu tôi cần tìm kiếm và JSON cho người khác, phải không?
ShuklaSannidhya

4
Đúng. bằng cách đó, bạn có được hiệu suất cần thiết từ việc tìm kiếm các trường dữ liệu trên mỗi cột và lấy blob JSON để sử dụng trong mã khi cần.
Nick Andriopoulos

9

Về cơ bản, mô hình đầu tiên bạn đang sử dụng được gọi là lưu trữ dựa trên tài liệu. Bạn nên xem qua cơ sở dữ liệu dựa trên tài liệu phổ biến của NoQuery như MongoDB và CouchDB . Về cơ bản, trong db dựa trên tài liệu, bạn lưu trữ dữ liệu trong các tệp json và sau đó bạn có thể truy vấn các tệp json này.

Mô hình thứ hai là cấu trúc cơ sở dữ liệu quan hệ phổ biến.

Nếu bạn muốn sử dụng cơ sở dữ liệu quan hệ như MySql thì tôi khuyên bạn chỉ nên sử dụng mô hình thứ hai. Không có điểm nào trong việc sử dụng MySql và lưu trữ dữ liệu như trong mô hình đầu tiên .

Để trả lời câu hỏi thứ hai của bạn, không có cách nào để truy vấn tên như 'foo' nếu bạn sử dụng mô hình đầu tiên .


Có phải là khôn ngoan để sử dụng cả hai mô hình? Khóa trên mỗi cột cho dữ liệu tôi cần tìm kiếm và JSON cho người khác (trong cùng một cơ sở dữ liệu)?
ShuklaSannidhya

@Sann - haha. Đó là sự trùng lặp dữ liệu. Bạn sẽ phải đảm bảo rằng cả hai phần dữ liệu luôn giống nhau. Ngay cả khi một trong những dữ liệu khác nhau tại bất kỳ thời điểm nào, thì dữ liệu của bạn không sạch và có thể dẫn đến vấn đề nghiêm trọng. Vì vậy, câu trả lời của tôi là KHÔNG
Girish

Nhưng sự dư thừa không tốn kém khi dữ liệu dư thừa nhỏ, giả sử, chỉ có hai trường mà tôi cần thực hiện tìm kiếm, vì vậy tôi tạo hai cột mới cho chúng, [có thể] xóa chúng khỏi dữ liệu JSON của tôi [/ có thể] . Đó sẽ không phải là sự trùng lặp tốn kém phải không?
ShuklaSannidhya

Nếu bạn đang xem hiệu năng, thì MongoDB và CouchDB cung cấp các hoạt động đọc và ghi nhanh hơn MySql vì chúng không cung cấp nhiều tính năng trong cơ sở dữ liệu quan hệ không cần thiết trong hầu hết các trường hợp sử dụng.
Girish

Không thể có lợi ích khi lưu trữ các đối tượng / cuộc gọi lại JSON từ API? Ví dụ: thay vì gọi API của youtube cho URL, ngón tay cái, v.v., bạn có thể truy vấn DB cục bộ (mysql, lite, v.v.) của mình cho đối tượng JSON không? Tôi không biết, có ý nghĩa với tôi, đặc biệt nếu bạn đang cố gắng lưu trữ bộ đệm hoặc làm cho ứng dụng chạy nhanh hơn. Nhưng tôi không chuyên nghiệp: /
markbratanov

4

Có vẻ như bạn chủ yếu do dự có nên sử dụng mô hình quan hệ hay không.

Như hiện tại, ví dụ của bạn sẽ phù hợp với một mô hình quan hệ một cách hợp lý, nhưng vấn đề có thể xảy ra tất nhiên khi bạn cần làm cho mô hình này phát triển.

Nếu bạn chỉ có một (hoặc một vài mức thuộc tính được xác định trước) cho thực thể chính (người dùng) của mình, bạn vẫn có thể sử dụng mô hình Giá trị thuộc tính thực thể (EAV) trong cơ sở dữ liệu quan hệ. (Điều này cũng có ưu và nhược điểm của nó.)

Nếu bạn dự đoán rằng bạn sẽ nhận được các giá trị ít cấu trúc hơn mà bạn muốn tìm kiếm bằng ứng dụng của mình, MySQL có thể không phải là lựa chọn tốt nhất ở đây.

Nếu bạn đang sử dụng PostgreSQL, bạn có khả năng có thể tận dụng tốt nhất cả hai thế giới. (Điều này thực sự phụ thuộc vào cấu trúc thực tế của dữ liệu ở đây ... MySQL cũng không hẳn là lựa chọn sai và các tùy chọn NoQuery có thể được quan tâm, tôi chỉ đề xuất các lựa chọn thay thế.)

Thật vậy, PostgreSQL có thể xây dựng chỉ mục trên các hàm (không thay đổi) (mà MySQL không thể biết được) và trong các phiên bản gần đây, bạn có thể sử dụng PLV8 trên dữ liệu JSON trực tiếp để xây dựng chỉ mục trên các yếu tố JSON quan tâm cụ thể, điều này sẽ cải thiện tốc độ truy vấn của bạn khi tìm kiếm dữ liệu đó.

BIÊN TẬP:

Vì sẽ không có quá nhiều cột mà tôi cần thực hiện tìm kiếm, nên sử dụng cả hai mô hình có khôn ngoan không? Khóa trên mỗi cột cho dữ liệu tôi cần tìm kiếm và JSON cho người khác (trong cùng một cơ sở dữ liệu MySQL)?

Trộn hai mô hình không nhất thiết sai (giả sử không gian thừa là không đáng kể), nhưng nó có thể gây ra sự cố nếu bạn không đảm bảo hai bộ dữ liệu được giữ đồng bộ: ứng dụng của bạn không bao giờ thay đổi một mà không cập nhật cái khác .

Một cách tốt để đạt được điều này là có một trình kích hoạt thực hiện cập nhật tự động, bằng cách chạy một thủ tục được lưu trữ trong máy chủ cơ sở dữ liệu bất cứ khi nào cập nhật hoặc chèn được thực hiện. Theo như tôi biết, ngôn ngữ thủ tục lưu trữ MySQL có thể thiếu hỗ trợ cho bất kỳ loại xử lý JSON nào. Một lần nữa PostgreSQL hỗ trợ PLV8 (và có thể cả RDBMS khác với các ngôn ngữ thủ tục được lưu trữ linh hoạt hơn) sẽ hữu ích hơn (tự động cập nhật cột quan hệ của bạn bằng cách sử dụng trình kích hoạt khá giống với cập nhật chỉ mục theo cách tương tự).


Ngoài những gì tôi đã nói ở trên, có thể đáng để xem xét các toán tử cho kiểu dữ liệu JSONB trong PostgreQuery 9.4 trở lên.
Bruno

1

một số thời gian tham gia trên bàn sẽ là một chi phí. hãy nói cho OLAP. nếu tôi có hai bảng thì một bảng là ORDERS và bảng khác là ORDER_DETAILS. Để có được tất cả các chi tiết đơn hàng, chúng tôi phải tham gia hai bảng, điều này sẽ làm cho truy vấn chậm hơn khi không có hàng nào trong bảng tăng lên, hãy nói hàng triệu hoặc hơn .. tham gia trái / phải quá chậm so với tham gia bên trong. Tôi nghĩ rằng nếu chúng ta thêm chuỗi JSON / Object trong mục ORDERS tương ứng thì THAM GIA sẽ tránh được. thêm tạo báo cáo sẽ nhanh hơn ...


1

Câu trả lời ngắn gọn bạn phải trộn lẫn giữa chúng, sử dụng json cho dữ liệu mà bạn sẽ không tạo mối quan hệ với chúng như dữ liệu liên hệ, địa chỉ, sản phẩm bị sai lệch


0

Bạn đang cố gắng điều chỉnh một mô hình không liên quan vào cơ sở dữ liệu quan hệ, tôi nghĩ bạn sẽ được phục vụ tốt hơn bằng cách sử dụng cơ sở dữ liệu NoQuery như MongoDB . Không có lược đồ được xác định trước phù hợp với yêu cầu của bạn là không giới hạn số lượng trường (xem ví dụ về bộ sưu tập MongoDB điển hình). Kiểm tra tài liệu MongoDB để có ý tưởng về cách bạn truy vấn tài liệu của mình, ví dụ:

db.mycollection.find(
    {
      name: 'sann'
    }
)

2
Vì tò mò, điều khiến bạn cho rằng mô hình của anh ấy không liên quan. Những thông tin anh đưa lên trên có vẻ rất liên quan đến tôi.
Colin M

0

Như những người khác đã chỉ ra các truy vấn sẽ chậm hơn. Thay vào đó, tôi khuyên bạn nên thêm ít nhất một cột '_ID' để truy vấn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.