成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

PostgreSQL 9.4 中使用 jsonb

張紅新 / 1711人閱讀

摘要:但這些不會(huì)發(fā)生列中,這種查找使用了索引,卻并沒有像使用表達(dá)式索引那樣將速度優(yōu)化的很好。這也使得數(shù)據(jù)驗(yàn)證和創(chuàng)建表單更加簡單。在每一個(gè)新版本中,使用和都比過去更加容易,變得更加出色。因此,嘗試使用最新的版本,付出總是會(huì)很快得到回報(bào)的。

轉(zhuǎn)載翻譯自http://nandovieira.com/using-postgresql-and-jsonb-with-ruby-on-rails

PostgreSQL 9.4 引入了jsonb,一個(gè)新的列類型用于存儲(chǔ)文檔到你的關(guān)系數(shù)據(jù)庫中。jsonbjson在更高的層面上看起來幾乎是一樣的,但在存儲(chǔ)實(shí)現(xiàn)上是不同的。

使用jsonb的優(yōu)勢(shì)在于你可以輕易的整合關(guān)系型數(shù)據(jù)和非關(guān)系型數(shù)據(jù),在性能方面,可以比大多數(shù)類似于MongoDB這樣的非關(guān)系數(shù)據(jù)庫更好

理解json和jsonb之間的不同

因此,兩種列類型之間的區(qū)別是什么?當(dāng)我們比較寫入數(shù)據(jù)速度時(shí),由于數(shù)據(jù)存儲(chǔ)的方式的原因,jsonb會(huì)比json稍微的慢一點(diǎn)。

json存儲(chǔ)完整復(fù)制過來的文本輸入,必須一遍又一遍的解析在你調(diào)用任何函數(shù)的時(shí)候。它不支持索引,但你可以為查詢創(chuàng)建表達(dá)式索引。

jsonb存儲(chǔ)的二進(jìn)制格式,避免了重新解析數(shù)據(jù)結(jié)構(gòu)。它支持索引,這意味著你可以不使用指定的索引就能查詢?nèi)魏温窂健?/p>

其他的不同包括,json列會(huì)每次都解析存儲(chǔ)的值,這意味著鍵的順序要和輸入的時(shí)候一樣。但jsonb不同,以二進(jìn)制格式存儲(chǔ)且不保證鍵的順序。因此,如果你有軟件需要依賴鍵的順序,jsonb可能不是你的應(yīng)用的最佳選擇。

讓我們運(yùn)行一個(gè)簡單的基準(zhǔn)測(cè)試。在這個(gè)例子中,我使用下面這樣一個(gè)json數(shù)據(jù)結(jié)構(gòu):

json{
  "twitter": "johndoe1",
  "github": "johndoe1",
  "bio": "Lorem ipsum dolor sit amet, consectetur adipisicing elit. Labore impedit 
          aliquam sapiente dolore magni aliquid ipsa ad, enim, esse ut reprehenderit 
          quaerat deleniti fugit eaque. Vero eligendi voluptatibus atque, asperiores.",
  "blog": "http://johndoe1.example.com",
  "interests": [
    "music",
    "movies",
    "programming"
  ],
  "age": 42,
  "newsletter": true
}

插入30000條完全一樣的記錄,我相信jsonb在插入復(fù)雜結(jié)構(gòu)時(shí)會(huì)慢一些。

Rehearsal ------------------------------------------------
insert jsonb   2.690000   0.590000   3.280000 ( >12.572343)
insert json    2.690000   0.590000   3.280000 ( 12.766534)
--------------------------------------- total: 6.560000sec

-----------------------------------------user     system      total        real
insert jsonb   2.680000   0.590000   3.270000 ( 13.206602)
insert json    2.650000   0.580000   3.230000 ( 12.577138)

真正的差距在查詢json/jsonb列的時(shí)候。首先讓我們看看這張表和索引。

sql
CREATE TABLE users ( id serial not null, settings jsonb not null default "{}", preferences json not null default "{}" ); CREATE INDEX settings_index ON users USING gin (settings); CREATE INDEX twitter_settings_index ON users ((settings->>"github")); CREATE INDEX preferences_index ON users ((preferences->>"github"));

注意我們有一個(gè)GIN索引在settings列上,兩個(gè)給出的路徑(github)表達(dá)式索引。在30000條數(shù)據(jù)中搜索Github用戶名為john30000的記錄(最后一個(gè)插入的記錄),會(huì)給出以下數(shù)字:

Rehearsal -----------------------------------------------------------------
read jsonb (index column)       0.030000   0.030000   0.060000 (  3.673465)
read jsonb (expression index)   0.010000   0.010000   0.020000 (  0.087105)
read json (expression index)    0.010000   0.020000   0.030000 (  0.080121)
read json (no index)            0.060000   0.030000   0.090000 (113.206747)
-------------------------------------------------------- total: 0.200000sec

-----------------------------------------user     system      total        real
read jsonb (index column)       0.010000   0.020000   0.030000 (  0.092476)
read jsonb (expression index)   0.010000   0.010000   0.020000 (  0.078916)
read json (expression index)    0.010000   0.010000   0.020000 (  0.081908)
read json (no index)            0.050000   0.040000   0.090000 (110.761944)

和你看到的那樣,表達(dá)式索引在兩種數(shù)據(jù)類型中的性能幾乎完全一樣,所以它們?cè)谶@里并沒有實(shí)際的意義。剩下的兩列不同的地方在于在查詢列時(shí)有沒有索引;jsonb能在整列建立GIN/GIST索引,而json不能建立這樣的索引。這也是為什么這json查詢速度這么慢的原因。

讓我們檢查下在沒有索引的情況下查詢分析器查詢數(shù)據(jù)。

sql
EXPLAIN SELECT * FROM users WHERE settings @> "{"twitter": "john30000"}" LIMIT 1; -- QUERY PLAN -- ------------------------------------------------------------------------------------- -- Limit (cost=28.23..31.96 rows=1 width=468) -- -> Bitmap Heap Scan on users (cost=28.23..140.07 rows=30 width=468) -- Recheck Cond: (settings @> "{"twitter": "john30000"}"::jsonb) -- -> Bitmap Index Scan on settings_index (cost=0.00..28.23 rows=30 width=0) -- Index Cond: (settings @> "{"twitter": "john30000"}"::jsonb) EXPLAIN SELECT * FROM users WHERE preferences->>"twitter" = "john30000" LIMIT 1; -- QUERY PLAN -- ------------------------------------------------------------------------- -- Limit (cost=0.00..25.23 rows=1 width=468) -- -> Seq Scan on users (cost=0.00..3784.00 rows=150 width=468) -- Filter: ((preferences ->> "twitter"::text) = "john30000"::text)

最重要的是,json做的是順序掃描,這意味著PostgreSQL將根據(jù)順序一條一條往下找,直到找到符合條件的數(shù)據(jù),同時(shí)記住查找這些數(shù)據(jù)時(shí),每條記錄中的JSON內(nèi)容都會(huì)被解析,這將導(dǎo)致在復(fù)雜結(jié)構(gòu)中查詢速度變慢。

但這些不會(huì)發(fā)生jsonb列中,這種查找使用了索引,卻并沒有像使用表達(dá)式索引那樣將速度優(yōu)化的很好。

jsonb有一個(gè)需要注意的點(diǎn)是,jsonb會(huì)一直順序檢索如果你使用->>操作符在一個(gè)沒有表達(dá)式索引的路徑上。

sql
EXPLAIN SELECT * FROM users WHERE settings->>"twitter" = "johndoe30000" LIMIT 1; -- QUERY PLAN -- ------------------------------------------------------------------------- -- Limit (cost=0.00..25.23 rows=1 width=468) -- -> Seq Scan on users (cost=0.00..3784.00 rows=150 width=468) -- Filter: ((settings ->> "twitter"::text) = "johndoe30000"::text) -- (3 rows)

因此,在你不提前知道查詢哪個(gè)json數(shù)據(jù)中的鍵或者查詢所有json路徑的情況下,請(qǐng)確保你定義了GIN/GIST索引和使用@>(或者其他有利于索引的操作符)

json轉(zhuǎn)化為jsonb

如果你已經(jīng)使用了json格式或者text格式的列存儲(chǔ)JSON數(shù)據(jù),你可以將他們轉(zhuǎn)化為jsonb,因而你可以依靠列索引。

sql
BEGIN; ALTER TABLE users ADD COLUMN preferences_jsonb jsonb DEFAULT "{}"; UPDATE users set preferences_jsonb = preferences::jsonb; ALTER TABLE users ALTER COLUMN preferences_jsonb SET NOT NULL; ALTER TABLE users RENAME COLUMN preferences TO preferences_json; ALTER TABLE users RENAME COLUMN preferences_jsonb TO preferences; -- Don"t remove the column until you"re sure everything is working. -- ALTER TABLE users DROP COLUMN preferences_json; COMMIT;

現(xiàn)在你已經(jīng)知道了json是如何工作的,讓我們看看在Ruby on Rails中是怎么使用的。

在Ruby on Rails中使用jsonb

Rails從4.2版本開始支持jsonb,使用他跟使用stringtext類型的列一樣簡單,在下面的代碼中,你將看到如何添加jsonb類型的列到已經(jīng)存在的表中。

ruby
# db/migrate/*_create_users.rb class CreateUsers < ActiveRecord::Migration def change enable_extension "citext" create_table :users do |t| t.text :name, null: false t.citext :username, null: false t.jsonb :preferences, null: false, default: "{}" end add_index :users, :preferences, using: :gin end end # db/migrate/*_add_jsonb_column_to_users.rb class AddJsonbColumnToUsers < ActiveRecord::Migration def change add_column :users, :preferences, :jsonb, null: false, default: "{}" add_index :users, :preferences, using: :gin end end

注意,我們已經(jīng)定義了GIN類型的索引,如果你想對(duì)給出的路徑創(chuàng)建表達(dá)式索引,你必須使用execute。在這個(gè)例子中,Rails不知道怎么使用ruby來轉(zhuǎn)化這個(gè)索引,所以你最好選擇將格式轉(zhuǎn)為SQL。

ruby
# config/initializers/active_record.rb Rails.application.config.active_record.schema_format = :sql # db/migrate/*_add_index_to_preferences_path_on_users.rb class AddIndexToPreferencesPathOnUsers < ActiveRecord::Migration def change execute <<-SQL CREATE INDEX user_prefs_newsletter_index ON users ((preferences->>"newsletter")) SQL end end

你的模型不需要做任何配置。你只需要?jiǎng)?chuàng)建支持json序列化的記錄來提供對(duì)象。

ruby
user = User.create!({ name: "John Doe", username: "johndoe", preferences: { twitter: "johndoe", github: "johndoe", blog: "http://example.com" } }) # Reload record from database to enforce serialization. user.reload # Show preferences. user.preferences #=> {"blog"=>"http://example.com", "github"=>"johndoe", "twitter"=>"johndoe"} # Get blog. user.preferences["blog"] #=> http://example.com

可以看到所有的鍵都是以string形式返回。你也可以使用通用的序列化方式,你就可以通過符號(hào)來訪問JSON對(duì)象。

ruby
# app/models/user.rb class User < ActiveRecord::Base serialize :preferences, HashSerializer end # app/serializers/hash_serializer.rb class HashSerializer def self.dump(hash) hash.to_json end def self.load(hash) (hash || {}).with_indifferent_access end end

另一個(gè)比較有意思的是ActiveRecord特性就是store_accessor。如果你更改一些屬性比較頻繁,你可以創(chuàng)建accessor,這樣你可以賦值給屬性來代替JSON傳值。這也使得數(shù)據(jù)驗(yàn)證和創(chuàng)建表單更加簡單。因此,如果我們創(chuàng)建一個(gè)表單來保存博客url、Github和Twitter賬戶,你可以像下面這樣使用:

ruby
class User < ActiveRecord::Base serialize :preferences, HashSerializer store_accessor :preferences, :blog, :github, :twitter end

現(xiàn)在你可以簡單的賦值給這些屬性了。

ruby
user = User.new(blog: "http://example.org", github: "johndoe") user.preferences #=> {"blog"=>"http://example.org", "github"=>"johndoe"} user.blog #=> http://example.org user.preferences[:github] #=> johndoe user.preferences["github"] #=> johndoe

定義了 store accessors 后,你可以像正常其他屬性一樣,定義數(shù)據(jù)驗(yàn)證和創(chuàng)建表單

查詢jsonb列

現(xiàn)在是時(shí)候使用一些查詢操作。關(guān)于PostgreSQL的更多操作,請(qǐng)閱讀完整的文檔列表

同時(shí),記得使用注釋你執(zhí)行的查詢語句;這有助于你更好的去做索引優(yōu)化。

訂閱新聞郵件的用戶

ruby
# preferences->newsletter = true User.where("preferences @> ?", {newsletter: true}.to_json)

對(duì)Ruby感興趣的用戶

ruby
# preferences->interests = ["ruby", "javascript", "python"] User.where("preferences -> "interests" ? :language", language: "ruby")

這個(gè)查詢不會(huì)用到列索引;如果你想查詢數(shù)組,請(qǐng)確保你創(chuàng)建了表達(dá)式索引。

ruby
CREATE INDEX preferences_interests_on_users ON users USING GIN ((preferences->"interests"))

設(shè)置了Twitter和Github賬號(hào)的用戶

ruby
# preferences->twitter AND preferences->github User.where("preferences ?& array[:keys]", keys: ["twitter", "github"])

設(shè)置Twitter或Github賬號(hào)的用戶

ruby
# preferences->twitter OR preferences->github User.where("preferences ?| array[:keys]", keys: ["twitter", "github"])

住在洛杉磯/加利福尼亞的用戶

ruby
# preferences->state = "SP" AND preferences->city = "S?o Paulo" User.where("preferences @> ?", {city: "San Francisco", state: "CA"}.to_json)
關(guān)于hstore

hstore列不允許嵌套的結(jié)構(gòu),它將所有的值以字符串形式存儲(chǔ),所以必須要在數(shù)據(jù)庫層或者應(yīng)用程序?qū)訉?shù)據(jù)強(qiáng)制轉(zhuǎn)化為字符串類型。而在json/jsonb類型的列上不會(huì)遇到這個(gè)問題,數(shù)值類型(integers/float),布爾類型,數(shù)組,字符串和空類型都可以接受,甚至你想的任何方式的數(shù)據(jù)嵌套。

因此推薦你盡早放棄hstore而去使用jsonb,但要記住的是你必須使用PostgreSQL 9.4以上版本才行。

我以前寫的hstore,想知道更多相關(guān)的內(nèi)容就點(diǎn)擊查看。

總結(jié)

PostgreSQL是一個(gè)非常強(qiáng)大的數(shù)據(jù)庫,幸運(yùn)的是ActiveRecord能跟上PostgreSQL的更新,為jsonb和hstore特性引入了內(nèi)置支持。

而像表達(dá)式索引這樣的支持也在不斷的改善。將ActiveRecord的序列化改為SQL沒什么大不了的,但卻使的索引變得更加簡單。

ruby
# This doesn"t exist, but it would be nice to have it! add_index :users, "(settings->>"github")", raw: true

在每一個(gè)新版本中,使用Rails和PostgreSQL都比過去更加容易,變得更加出色。因此,嘗試使用最新的Rails版本,付出總是會(huì)很快得到回報(bào)的。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/38925.html

相關(guān)文章

  • CentOS 7 安裝 PostgreSQL 11

    摘要:環(huán)境阿里云是以加州大學(xué)伯克利分校計(jì)算機(jī)系開發(fā)的,現(xiàn)在已經(jīng)更名為,版本為基礎(chǔ)的對(duì)象關(guān)系型數(shù)據(jù)庫管理系統(tǒng)。支持大部分標(biāo)準(zhǔn)并且提供了許多其他現(xiàn)代特性復(fù)雜查詢外鍵觸發(fā)器視圖事務(wù)完整性。 CentOS 7 安裝 PostgreSQL 11 showImg(https://segmentfault.com/img/bVbq6cs?w=1348&h=347); PostgreSQL: The Wor...

    lolomaco 評(píng)論0 收藏0
  • CentOS 7 安裝 PostgreSQL 11

    摘要:環(huán)境阿里云是以加州大學(xué)伯克利分校計(jì)算機(jī)系開發(fā)的,現(xiàn)在已經(jīng)更名為,版本為基礎(chǔ)的對(duì)象關(guān)系型數(shù)據(jù)庫管理系統(tǒng)。支持大部分標(biāo)準(zhǔn)并且提供了許多其他現(xiàn)代特性復(fù)雜查詢外鍵觸發(fā)器視圖事務(wù)完整性。 CentOS 7 安裝 PostgreSQL 11 showImg(https://segmentfault.com/img/bVbq6cs?w=1348&h=347); PostgreSQL: The Wor...

    yuanxin 評(píng)論0 收藏0
  • PostgreSQL JSONB 使用入門

    摘要:類型說明根據(jù)中的說明,數(shù)據(jù)類型是用來存儲(chǔ)數(shù)據(jù)的。它們幾乎接受完全相同的值集合作為輸入。該結(jié)構(gòu)是非強(qiáng)制的,但是有一個(gè)可預(yù)測(cè)的結(jié)構(gòu)會(huì)使集合的查詢更容易。如中,表示在和這兩個(gè)位置出現(xiàn)過,在中這些位置實(shí)際上就是元組的行號(hào),包括數(shù)據(jù)塊以及。 json 類型 說明 根據(jù)RFC 7159中的說明,JSON 數(shù)據(jù)類型是用來存儲(chǔ) JSON(JavaScript Object Notation)數(shù)據(jù)的。這...

    Karrdy 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<