JSON'u veritabanında depolamak ve her anahtar için yeni bir sütun oluşturmak


214

Masamda kullanıcı ile ilgili verileri depolamak için aşağıdaki modeli uyguluyorum - 2 sütun - uid(birincil anahtar) ve metakullanıcı hakkındaki diğer verileri JSON biçiminde saklayan bir sütun var.

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

Bu daha iyi bir yolu var mı (epeyce, tasarım bakımından) masa gibi birçok sütunlar bulunur tek sütunluk başına mülkiyet modeli, daha uid, name, emailid.

İlk model hakkında sevdiğim şey, sınırlama olmadan olabildiğince çok alan ekleyebilirsiniz.

Ayrıca, ilk modeli uyguladığımı merak ediyordum. Üzerinde 'foo' gibi bir adı olan tüm kullanıcıları getirmek istiyorum, üzerinde nasıl bir sorgu yapabilirim?

Soru - JSON veya alan başına sütun kullanarak, kullanıcı ile ilgili verileri (alan sayısının sabit olmadığını akılda tutarak) depolamanın en iyi yolu hangisidir? Ayrıca, ilk model uygulanırsa, veritabanı yukarıda açıklandığı gibi nasıl sorgulanır? Her iki modeli de, sorgu tarafından aranabilecek tüm verileri ayrı bir satırda ve diğer verileri JSON'da (farklı bir satırdır) depolayarak kullanmalı mıyım?


Güncelleme

Üzerinde arama yapmam gereken çok fazla sütun olmayacağından, her iki modeli de kullanmak akıllıca mı? Aramam gereken veriler için sütun başına anahtar ve diğerleri için JSON (aynı MySQL veritabanında)?


40
harika bir soru! ama neden bir cevabı kabul etmedin? ki (benim gibi) diğer kullanıcılara yardımcı olur
Sahar Ch.

Yanıtlar:


198

Güncelleme: 4 Haziran 2017

Bu soru / cevabın biraz popülerlik kazandığı göz önüne alındığında, bir güncellemeye değer olduğunu düşündüm.

Bu soru ilk olarak gönderildiğinde, MySQL'in JSON veri türleri için desteği yoktu ve PostgreSQL'deki destek başlangıç ​​aşamasındaydı. 5.7'den beri, MySQL artık bir JSON veri tipini (ikili depolama biçiminde) desteklemektedir ve PostgreSQL JSONB önemli ölçüde olgunlaşmıştır. Her iki ürün de, JSON nesnesinin belirli anahtarlarını dizine ekleme desteği de dahil olmak üzere, rasgele belgeleri saklayabilen performans gösteren JSON türleri sağlar.

Ancak, yine de ilişkisel bir veritabanı kullanırken, varsayılan tercihinizin yine de değer başına sütun olması gerektiğini belirten orijinal ifademin yanında duruyorum. İlişkisel veritabanları, hala içindeki verilerin oldukça iyi normalleştirileceği varsayımı üzerine inşa edilmiştir. Sorgu planlayıcı, sütunlara bakarken bir JSON belgesindeki anahtarlara bakarken olduğundan daha iyi optimizasyon bilgisine sahiptir. Yabancı anahtarlar sütunlar arasında oluşturulabilir (ancak JSON belgelerindeki anahtarlar arasında oluşturulamaz). Önemli: şemanızın çoğunluğu JSON kullanarak haklı çıkacak kadar değişkense, en azından ilişkisel bir veritabanının doğru seçim olup olmadığını düşünmek isteyebilirsiniz.

Bununla birlikte, az sayıda uygulama mükemmel bir şekilde ilişkisel veya belge yönelimlidir. Çoğu uygulama her ikisinin bir karışımına sahiptir. Şahsen JSON ilişkisel bir veritabanında yararlı buldum bazı örnekler:

  • Bir kişinin e-posta adreslerini ve telefon numaralarını depolarken, bu adresleri bir JSON dizisine değer olarak depolamak, birden fazla ayrı tablodan çok daha kolaydır

  • Rasgele anahtar / değer kullanıcı tercihlerini kaydetme (değerin mantıksal, metinsel veya sayısal olabileceği ve farklı veri türleri için ayrı sütunlar olmasını istemediğiniz)

  • Tanımlanmış şeması olmayan yapılandırma verilerini saklama (Zapier veya IFTTT oluşturuyorsanız ve her entegrasyon için yapılandırma verilerini depolamanız gerekiyorsa)

Eminim başkaları da vardır, ancak bunlar sadece birkaç hızlı örnektir.

Orijinal Yanıt

Sınırlama olmaksızın (isteğe bağlı belge boyutu sınırı dışında) istediğiniz kadar alan ekleyebilmek istiyorsanız, MongoDB gibi bir NoSQL çözümü düşünün.

İlişkisel veritabanları için: değer başına bir sütun kullanın. Bir sütuna bir JSON blob koymak sorgulamak neredeyse imkansız hale getirir (ve gerçekten çalışan bir sorgu bulduğunuzda acı yavaş).

İlişkisel veritabanları, dizin oluştururken veri türlerinden yararlanır ve normalleştirilmiş bir yapı ile uygulanması amaçlanır .

Bir yan not olarak: bu JSON'u asla ilişkisel bir veritabanında saklamamanız gerektiği anlamına gelmez. Gerçek meta veriler ekliyorsanız veya JSON'unuz sorgulanması gerekmeyen ve yalnızca görüntüleme için kullanılan bilgileri açıklıyorsa , tüm veri noktaları için ayrı bir sütun oluşturmak aşırıya kaçabilir.


1
Arama yapmam gereken çok fazla sütun olmayacağından, her iki modeli de kullanmak akıllıca mı? Aramam gereken veriler için sütun başına anahtar ve diğerleri için JSON (aynı MySQL veritabanında)?
ShuklaSannidhya

3
@Sann Sık sık okumak veya sorgulamak istediğiniz veriler için değer başına bir sütun kullanmalısınız . Birisinin adını JSON'a koymak mantıklı değildir, çünkü buna dayanarak sorgulama olasılığınız olmasa bile, çok sık ihtiyacınız olacaktır . Bu, uygulama tarafınızda çok fazla israf çözme. Eğer sürece gerçekten veri daha iyi JSON olarak temsil edilir gibi hissetmek (bana ve güven, muhtemelen değil), bunun başvurmak gerekir.
Colin M

5
" virtually impossible to query" - bugün psql jsonb
ted

1
@ted true. Ancak, bu cevabı yazarken gerçekten mevcut değildi. Ayrıca, bu soru, yeteneği olmayan MySQL ile ilgilidir.
Colin M

3
@ColinM, evet, yorumumun yayınınızın 3 yıl daha genç olduğunu anlıyorum. Ayrılmamın nedeni, başkaları için faydalı ve karar değiştirebilmesidir. MySQL referansına gelince: doğru olabilir, ancak "For relational databases"cevabınız var = P
ted

69

Çoğu şey gibi "o bağlıdır". Verileri sütunlarda veya JSON'da saklamak doğru veya yanlış / iyi veya kötü değil. Daha sonra ne yapmanız gerektiğine bağlıdır. Bu verilere erişmek için tahmin ettiğiniz yol nedir? Diğer verileri çapraz referans almanız gerekecek mi?

Diğer insanlar teknik değiş tokuşun ne olduğunu çok iyi cevapladılar.

Birçok kişi uygulamanızın ve özelliklerin zaman içinde geliştiğini ve bu veri depolama kararının ekibinizi nasıl etkilediğini tartışmadı.

JSON kullanmanın caziplerinden biri şemayı taşımayı önlemek olduğundan ve eğer takım disiplinli değilse, başka bir anahtar / değer çiftini JSON alanına yapıştırmak çok kolaydır. Bunun için bir göç yok, kimse bunun ne için olduğunu hatırlamıyor. Üzerinde doğrulama yok.

Ekibim JSON'u postgreslerde yan geleneksel sütunlar boyunca kullandı ve ilk başta dilimlenmiş ekmeklerden beri en iyi şeydi. JSON çekici ve güçlüydü, bir güne kadar esnekliğin bir bedeli olduğunu fark ettik ve aniden gerçek bir acı noktası oldu. Bazen bu nokta gerçekten hızlı bir şekilde sürünür ve sonra değiştirmek zorlaşır, çünkü bu tasarım kararının üzerine çok daha fazla şey inşa ettik.

Fazla mesai, yeni özellikler ekleme, JSON'daki verilerin bulunması, geleneksel sütunlara yapışırsak eklenmiş olabileceklerden daha karmaşık görünümlü sorgulara yol açtı. Sonra, bazı önemli değerleri tekrar sütunlara ayırmaya başladık, böylece birleşimler yapabilir ve değerler arasında karşılaştırmalar yapabiliriz. Kötü bir fikir. Şimdi çoğaltma yaptık. Yeni bir geliştirici devreye girecek ve kafanız karışacak mı? Geri kaydetmemiz gereken değer nedir? JSON biri mi sütun mu?

JSON alanları bunun ve bunun küçük parçaları için önemsiz çekmeceler haline geldi. Veritabanı düzeyinde veri doğrulaması yok, belgeler arasında tutarlılık veya bütünlük yok. Bu, geleneksel sütunlardan zor tür ve kısıt denetimi almak yerine tüm bu sorumluluğu uygulamaya itti.

Geriye dönüp baktığımızda JSON, çok hızlı bir şekilde tekrarlamamızı ve kapıdan bir şey çıkarmamızı sağladı. Harikaydı. Bununla birlikte, belirli bir takım boyutuna ulaştıktan sonra esneklik, kendimizi uzun bir teknik borç ipi ile asmamıza izin verdi ve bu da daha sonraki özelliklerin gelişimini yavaşlattı. Dikkatle kullanın.

Verilerinizin niteliği hakkında uzun ve sıkı düşünün. Uygulamanızın temelidir. Veriler zaman içinde nasıl kullanılacaktır. Peki nasıl değişecek?


7
"esneklik de bize teknik borç uzun bir ip ile asmak için izin verdi" çok güzel bir metafor!
Antoine Gallix

Uzun yıllar kalkınma ve farklı insanlarla çalıştıktan sonra, bu konu hakkında yazmalıyım, aynı şeyi yazacağım. Şu anda o kadar çok geliştirici var ki, birçoğu yılların tecrübesiyle bile aslında seviye atlamıyorlar. Her şeyi basit tutmalıyız ve benim için her zaman dikkate almamız gereken iki şey, başarıyı "çerçeveleyebilir", kodun ölçeklenebilirliği ve sürdürülebilirliği.
JohnnyJaxs

27

Sadece orada savurmak, ama WordPress bu tür şeyler için bir yapıya sahiptir (en azından WordPress gözlemlediğim ilk yerdi, muhtemelen başka bir yerden kaynaklandı).

Sınırsız tuşlara izin verir ve arama yapmak bir JSON blob kullanmaktan daha hızlıdır, ancak bazı NoSQL çözümleri kadar hızlı değildir.

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

DÜZENLE

Geçmişi / çoklu anahtarları kaydetmek için

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
.................

ve böyle bir şeyle sorgula:

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

1
Bir NoSQL çözüm gerçekten düzgün bir dizin anahtarında ilişkisel bir sorgu daha iyi performans olup olmadığını merak ediyorum. Bunun 1 seviyeli bir örnekte aşağı yukarı aynı olması gerektiğinden şüpheleniyordum.
Bruno

+1. Ben de fark ettim! Ancak size büyük bir tablo verir (satırlar açısından). Ayrıca yapabilirsiniz değil kullanıcı onun / onun adını değiştirirse, demek, birden fazla değer depolamak, ama çok eski adını korumaya yönelik bu durumda ben JSON tipi veri modelini gerekir istiyorum.
ShuklaSannidhya

@Sann, eski değeri JSON'da tutmak istiyorsanız, anahtarı da yeniden adlandırmanız gerekir: bir EAV (bu örneğin ne olduğu) veya JSON ile yapabilirsiniz. Özellikle farklı değil.
Bruno

Size büyük bir tablo verir, ancak yinelenen değerlere gelince, JSON ile aynı sorunla karşılaşırsınız - aynı düzeyde yinelenen anahtarlara sahip olamazsınız (örneğin iki "ad" anahtarı) ve öngörülebilir davranış bekleyebilirsiniz.
Adam

Elbette yinelenen anahtarlarınız olamaz, ancak bu anahtarla ilişkilendirilmiş bir dizi olabilir. Check out emailidSorumun içinde verdiğim örnekteki anahtarı.
ShuklaSannidhya

13

yaklaşımın dezavantajı tam olarak bahsettiğiniz şeydir:

her seferinde metin araması yapmanız gerektiğinden, bir şeyler bulmayı ÇOK yavaşlatır.

sütun başına değer tüm dizeyle eşleşir.

Yaklaşımınız (JSON tabanlı veriler), arama yapmanız gerekmeyen veriler için uygundur ve normal verilerinizle birlikte görüntülemeniz yeterlidir.

Düzenleme: Sadece açıklığa kavuşturmak için, yukarıdaki klasik ilişkisel veritabanları için gider. NoSQL, JSON'u dahili olarak kullanır ve istenen davranış bu ise muhtemelen daha iyi bir seçenektir.


1
Yani her ikisini de kullanmalıyım. Aramam gereken veriler için sütun başına anahtar, diğerleri için JSON, değil mi?
ShuklaSannidhya

4
Evet. bu şekilde, sütun başına veri alanlarını arayarak gerekli performansı elde edersiniz ve gerektiğinde kodda kullanmak için JSON blobunu alırsınız.
Nick Andriopoulos

9

Temel olarak, kullandığınız ilk modele belge tabanlı depolama denir. MongoDB ve CouchDB gibi popüler NoSQL belge tabanlı veritabanına bir göz atmalısınız . Temel olarak, belge tabanlı db'lerde, verileri json dosyalarında depolar ve daha sonra bu json dosyalarında sorgulayabilirsiniz.

İkinci model popüler ilişkisel veritabanı yapısıdır.

MySql gibi ilişkisel veritabanı kullanmak istiyorsanız, sadece ikinci modeli kullanmanızı öneririm. MySql kullanmanın ve veriyi ilk modelde olduğu gibi kaydetmenin bir anlamı yok .

İkinci sorunuzu cevaplamak için, ilk modeli kullanıyorsanız adı 'foo' gibi sorgulamanın bir yolu yoktur .


Her iki modeli de kullanmak akıllıca mı? Aramam gereken veriler için sütun başına anahtar ve diğerleri için JSON (aynı veritabanında)?
ShuklaSannidhya

@Sann - haha. Bu veri çoğaltma. Her iki veri parçasının da her zaman aynı olduğundan emin olmanız gerekir. Verilerden biri herhangi bir zamanda farklı olsa bile, verileriniz temiz değildir ve ciddi soruna yol açabilir. Cevabım HAYIR
Girish

Ancak fazlalık verileri küçük olduğunda artıklık pahalı değildir, diyelim ki, arama yapmam gereken sadece iki alan var, bu yüzden onlar için iki yeni sütun oluşturuyorum, [belki] bunları JSON verilerimden kaldır [/ belki] . Bu pahalı bir çoğaltma olmayacak değil mi?
ShuklaSannidhya

Performansa bakıyorsanız, MongoDB ve CouchDB, MySql'den daha hızlı okuma ve yazma işlemleri sağlar, çünkü ilişkisel veritabanlarında kullanım durumlarının çoğunda gerekli olmayan çok fazla özellik sunmazlar.
Girish

Avantaj, bir API'dan JSON nesnelerini / geri çağrıları depolamak olamaz mı? Örneğin, youtube API'sini URL, başparmak vb. İçin çağırmak yerine, yerel DB'nizi (mysql, lite, vb.) JSON nesnesi için sorgulayabilirsiniz? Bilmiyorum, özellikle de bir uygulamayı daha hızlı önbelleğe almaya veya çalıştırmaya çalışıyorsanız bana mantıklı geliyor. Ama ben profesyonel değilim: /
markbratanov

4

Görünüşe göre ilişkisel bir model kullanıp kullanmayacağınız konusunda tereddüt ediyorsunuz.

Bu haliyle, örneğin, ilişkisel bir modele makul derecede iyi uyuyor, ancak bu modeli geliştirmeniz gerektiğinde sorun elbette gelebilir.

Ana varlığınız (kullanıcı) için yalnızca bir (veya önceden belirlenmiş birkaç) özellik özniteliğiniz varsa, yine de ilişkisel veritabanında bir Varlık Özellik Değeri (EAV) modeli kullanabilirsiniz. (Bunun da artıları ve eksileri vardır.)

Uygulamanızı kullanarak aramak isteyeceğiniz daha az yapılandırılmış değerler alacağınızı tahmin ediyorsanız, MySQL burada en iyi seçenek olmayabilir.

PostgreSQL kullanıyorsanız, her iki dünyanın da en iyisini elde edebilirsiniz. (Bu gerçekten buradaki verilerin gerçek yapısına bağlıdır ... MySQL de yanlış bir seçim değildir ve NoSQL seçenekleri ilgi çekici olabilir, sadece alternatifler öneriyorum.)

Gerçekten de, PostgreSQL (değişmez) işlevler (MySQL'in bildiğim kadarıyla yapamayacağı) üzerinde dizin oluşturabilir ve son sürümlerde PLV8'i doğrudan JSON verilerinde kullanabilirsiniz ilgili JSON öğelerinde dizin oluşturmak için kullanabilirsiniz. bu verileri ararken sorgularınızın hızı.

DÜZENLE:

Üzerinde arama yapmam gereken çok fazla sütun olmayacağından, her iki modeli de kullanmak akıllıca mı? Aramam gereken veriler için sütun başına anahtar ve diğerleri için JSON (aynı MySQL veritabanında)?

İki modelin karıştırılması her zaman yanlış değildir (fazladan alanın önemsiz olduğu varsayılarak), ancak iki veri kümesinin senkronize olduğundan emin değilseniz sorunlara neden olabilir: uygulamanız diğerini de güncellemeden asla değiştirmemelidir .

Bunu başarmanın iyi bir yolu, bir güncelleme veya ekleme yapıldığında veritabanı sunucusunda saklı bir yordam çalıştırarak bir tetikleyicinin otomatik güncellemeyi gerçekleştirmesidir. Bildiğim kadarıyla, MySQL saklı yordam dili muhtemelen her türlü JSON işleme desteği yoktur. Yine PLV8 destekli PostgreSQL (ve muhtemelen daha esnek saklı yordam dilleri olan diğer RDBMS) daha yararlı olmalıdır (bir tetikleyici kullanarak ilişkisel sütununuzu otomatik olarak güncellemek, bir dizini aynı şekilde güncellemeye oldukça benzer).


Yukarıda söylediğim yanı sıra, PostgreSQL 9.4 ve üstü JSONB veri türü için operatörlere bakmaya değer olabilir.
Bruno

1

bazı zamanlar masaya katılır ek yük olacak. OLAP diyelim. iki tablo varsa bir ORDERS tablo ve diğer ORDER_DETAILS. Tüm sipariş detaylarını almak için iki tabloya katılmak zorundayız, bu tablolardaki satırların hiçbiri milyonlarca ya da daha fazla artmaya izin verdiğinde sorguyu yavaşlatacaktır. Sol / sağ birleşim iç birleşimden çok daha yavaştır. Ben ilgili ORDERS giriş JOIN JSON dize / nesne eklerseniz kaçınılması gerektiğini düşünüyorum. rapor oluşturma daha hızlı olacak ...


1

aralarında karıştırmanız gereken kısa yanıt, iletişim bilgileri, adres, ürün değişkenleri gibi onlarla ilişki kurmayacağınız veriler için json kullanın


0

İlişkisel bir veritabanına ilişkisel olmayan bir model sığdırmaya çalışıyorsunuz, bence MongoDB gibi bir NoSQL veritabanı kullanarak daha iyi hizmet edersiniz . Alan sayısı ile ilgili herhangi bir kısıtlamaya sahip olmama gereksiniminize uyan önceden tanımlanmış bir şema yoktur (tipik MongoDB toplama örneğine bakın). Belgelerinizi nasıl sorgulayacağınız hakkında bir fikir edinmek için MongoDB belgelerine bakın, ör.

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

2
Meraktan, modelinin ilişkisel olmadığını varsaymanızı sağlayan şey. Yukarıda verdiği bilgiler benimle çok ilişkisel görünüyor.
Colin M

0

Diğerlerinin de belirttiği gibi, sorgular daha yavaş olacaktır. Bunun yerine sorgulamak için en az bir '_ID' sütunu eklemenizi öneririm.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.