2018年5月23日 星期三
How to Create Postgres Indexes Concurrently in ActiveRecord Migrations
之前的文章提到要避免 deployment downtime 其中一個方式是 create index concurrently
http://everyday1percent.blogspot.com/2018/05/rails-deployment-downtime.html
原來 rails 有內建的 API 做到這件事
```ruby
class AddIndexToBuildMetricIdOnBuilds < ActiveRecord::Migration
disable_ddl_transaction!
def change
add_index :builds, :build_metric_id, :algorithm => :concurrently
end
end
```
好潮。
https://robots.thoughtbot.com/how-to-create-postgres-indexes-concurrently-in
https://semaphoreci.com/blog/2017/06/21/faster-rails-indexing-large-database-tables.html
2018年5月10日 星期四
Query jsonb data type in postgres with Rails
https://nandovieira.com/using-postgresql-and-jsonb-with-ruby-on-rails
2018年5月1日 星期二
PG::ConnectionBad: FATAL: Peer authentication failed for user XXX
sudo vim /etc/postgresql/9.5/main/pg_hba.conf
加一行:
```
local all your_user_name trust
```
編輯
```
# DO NOT DISABLE!
# If you change this first entry you will need to make sure that the
# database superuser can access the database using some other method.
# Noninteractive access to all databases is required during automatic
# maintenance (custom daily cronjobs, replication, and similar tasks).
#
# Database administrative login by Unix domain socket
local all postgres peer
# TYPE DATABASE USER ADDRESS METHOD
local all your_user_name trust
# "local" is for Unix domain socket connections only
local all all peer
# IPv4 local connections:
host all all 127.0.0.1/32 md5
# IPv6 local connections:
host all all ::1/128 md5
# Allow replication connections from localhost, by a user with the
# replication privilege.
#local replication postgres peer
#host replication postgres 127.0.0.1/32 md5
```
然後重啟 server
sudo service postgresql restart
https://www.postgresql.org/docs/9.3/static/auth-methods.html
2018年4月26日 星期四
How rails store your migration history
有時候手動操作 database 刪掉了 table,於是乎 rails migration 跟 database 就對不起來了,要怎麼樣騙 rails 說上一個 create table 的 migration file 沒跑過呢?how does rails track this?
Ans: Rails 存了一個 schema_migrations 的 table 在database 裡面,只要把相對應的 record 刪掉就行了,easy peasy
https://stackoverflow.com/questions/12057408/how-does-rails-keep-track-of-which-migrations-have-run-for-a-database
2018年4月2日 星期一
釐清 DatabaseCleaner strategy 跟 clean_with
```rb
config.before(:suite) do
DatabaseCleaner.strategy = :transaction
DatabaseCleaner.clean_with(:truncation)
end
```
### 釐清 strategy 跟 clean_with
上面這段 code 指的是「當 test 開始時,用 truncation,然後之後都用 transaction」
為什麼要這樣呢?因為 truncation 比較慢 transaction 比較快
所以這就引發另一個問題,什麼是 truncation 什麼是 transaction?還有什麼其他的 strategy?
## Strategy
https://github.com/DatabaseCleaner/database_cleaner#what-strategy-is-fastest
可以選擇:
* transaction (優先使用)
* truncation
* deletion
這些 startegy 之間的差異:
https://stackoverflow.com/questions/10904996/difference-between-truncation-transaction-and-deletion-database-strategies
* transaction (優先使用): 代表把執行的 SQL 包在 `BEGIN TRANSACTION` 裡然後用 `ROLLBACK` 回逤成初始狀態
* truncation: 使用SQL `TRUNCATE TABLE` 直接把 table 清空
* deletion: 使用 SQL `DELETE FROM` 來刪除資料,速度慢
關於 delete 與 truncate:
https://dba.stackexchange.com/questions/30325/delete-vs-truncate
https://stackoverflow.com/questions/139630/whats-the-difference-between-truncate-and-delete-in-sql/12900557#12900557
有時間再寫關於 delete 與 truncate 之間的差別。
2018年2月22日 星期四
用 foreign_key 確保資料相依正確性 (referential integrity)
Rails 4.2 以後開始支援 database 的 foreign key,很好的文章:
https://robots.thoughtbot.com/referential-integrity-with-foreign-keys
我們會用
```rb
class User < ActiveRecord::Base
has_many :posts, dependent: :destroy
end
class Post < ActiveRecord::Base
belongs_to :user
validates :user, presence: true
end
```
這種做法來確保 post 有 user,看似沒問題但這些 validation 都在 rails 的 application 層級,而 rails 提供了很多方式讓你跳過這些 validations 和 callbacks,所以是不可信任的。ex:
User.delete_all 會跳過 `dependent: :destroy` callback
更別提我們常常連 `dependent: :destroy` callback 都忘記加(oops~)
所以應該讓 database 加上這個限制確保我們不會不小心搞爆這些資料
```rb
def change
add_foreign_key :posts, :users
end
```
另外也可以讓 database 幫我們做到 `dependent: destroy` 一樣的事情
```rb
add_foreign_key :posts, :users, on_delete: :cascade
```
會產生 SQL (postgres):
```sql
ALTER TABLE `posts`
ADD CONSTRAINT `posts_user_id_fk`
FOREIGN KEY (`user_id`) REFERENCES `users`(id)
ON DELETE CASCADE;
```
Caveats:rails 的 Polymorphic associations 不適用
https://robots.thoughtbot.com/referential-integrity-with-foreign-keys
我們會用
```rb
class User < ActiveRecord::Base
has_many :posts, dependent: :destroy
end
class Post < ActiveRecord::Base
belongs_to :user
validates :user, presence: true
end
```
這種做法來確保 post 有 user,看似沒問題但這些 validation 都在 rails 的 application 層級,而 rails 提供了很多方式讓你跳過這些 validations 和 callbacks,所以是不可信任的。ex:
User.delete_all 會跳過 `dependent: :destroy` callback
更別提我們常常連 `dependent: :destroy` callback 都忘記加(oops~)
所以應該讓 database 加上這個限制確保我們不會不小心搞爆這些資料
```rb
def change
add_foreign_key :posts, :users
end
```
另外也可以讓 database 幫我們做到 `dependent: destroy` 一樣的事情
```rb
add_foreign_key :posts, :users, on_delete: :cascade
```
會產生 SQL (postgres):
```sql
ALTER TABLE `posts`
ADD CONSTRAINT `posts_user_id_fk`
FOREIGN KEY (`user_id`) REFERENCES `users`(id)
ON DELETE CASCADE;
```
Caveats:rails 的 Polymorphic associations 不適用
2017年12月10日 星期日
PG::ConnectionBad: could not connect to server
今天遇到一個問題
我安裝 postgresapp 並執行了 server 但是 rails 找不到並噴了下面的 error:
```
➜ rails db:create
could not connect to server: No such file or directory
Is the server running locally and accepting
connections on Unix domain socket "/var/pgsql_socket/.s.PGSQL.5432"?
Couldn't create database for {"adapter"=>"postgresql", "encoding"=>"unicode", "pool"=>5, "database"=>"crypto_trader_development"}
rails aborted!
PG::ConnectionBad: could not connect to server: No such file or directory
Is the server running locally and accepting
connections on Unix domain socket "/var/pgsql_socket/.s.PGSQL.5432"?
```
首先檢查 socket 有沒有開:
```
➜ netstat -ln | grep PGSQL
92b93fdd84f73f stream 0 0 92b93fcc8bd9ef 0 0 0 /tmp/.s.PGSQL.5432
```
看起來是有,但是位置不對, rails 找的位置是 `/var/pgsql_socket/.s.PGSQL.5432` 然而實際位置是 `/tmp/.s.PGSQL.5432`
網路上很多解決方法,例如
加上 symlink
改 postgres 的 config 檔案
但後來我只是加了一行到 `.zshrc` 就解決了
```
https://github.com/PostgresApp/PostgresApp/issues/48#issuecomment-285452823
我安裝 postgresapp 並執行了 server 但是 rails 找不到並噴了下面的 error:
```
➜ rails db:create
could not connect to server: No such file or directory
Is the server running locally and accepting
connections on Unix domain socket "/var/pgsql_socket/.s.PGSQL.5432"?
Couldn't create database for {"adapter"=>"postgresql", "encoding"=>"unicode", "pool"=>5, "database"=>"crypto_trader_development"}
rails aborted!
PG::ConnectionBad: could not connect to server: No such file or directory
Is the server running locally and accepting
connections on Unix domain socket "/var/pgsql_socket/.s.PGSQL.5432"?
```
首先檢查 socket 有沒有開:
```
➜ netstat -ln | grep PGSQL
92b93fdd84f73f stream 0 0 92b93fcc8bd9ef 0 0 0 /tmp/.s.PGSQL.5432
```
看起來是有,但是位置不對, rails 找的位置是 `/var/pgsql_socket/.s.PGSQL.5432` 然而實際位置是 `/tmp/.s.PGSQL.5432`
網路上很多解決方法,例如
加上 symlink
改 postgres 的 config 檔案
但後來我只是加了一行到 `.zshrc` 就解決了
```
export PGHOST=localhost
```https://github.com/PostgresApp/PostgresApp/issues/48#issuecomment-285452823
2017年10月25日 星期三
database 到底要不要對 boolean 值打 index 呢?
真實情況是: it depends
but depends on what?
主要可以根據以下兩個基本的原則來 depends
boolean index 會有效能上幫助的時機:
1. 跟其他欄位一起打 index
2. 某一部分的 records 暫少數的時候,例如 true 的有 9000 筆而 false 只有 100 筆
https://stackoverflow.com/questions/42972726/postgres-sql-create-index-for-boolean-column
https://stackoverflow.com/questions/10524651/is-there-any-performance-gain-in-indexing-a-boolean-field
but depends on what?
主要可以根據以下兩個基本的原則來 depends
boolean index 會有效能上幫助的時機:
1. 跟其他欄位一起打 index
2. 某一部分的 records 暫少數的時候,例如 true 的有 9000 筆而 false 只有 100 筆
https://stackoverflow.com/questions/42972726/postgres-sql-create-index-for-boolean-column
https://stackoverflow.com/questions/10524651/is-there-any-performance-gain-in-indexing-a-boolean-field
2017年10月10日 星期二
reddit 上的 rails performance 總結
The Complete Guide to Rails Performance 這本書 https://www.railsspeed.com/ 的總結:
https://www.reddit.com/r/rails/comments/5x0wd2/has_anybody_purchased_the_complete_guide_to_rails/
- Measure. If you have a serious project a cost of NewRelic account is negligible but insights it provides are very valuable. Make sure you measure request queue time, when you have large variance in request processing time (some are slow, some are fast) you may want to switch from Nginx to Haproxy as a front-end.
- Cache output. With large JSON/HTML responses you'll move from 300ms to sub-10ms. Use Russian doll caching
- Use conditional GET
- Use proper DB indexes. If they aren't enough, denormalize in RDBMS or use Shopify's
identity_cache. There are gems that suggest what indexes you are missing. Remember that indexes are useful when searching using a prefix of indexed columns, so if you have an index on(name, created_at)it will be used when searching or sorting byname. Read "Use the index Luke" it contains pretty much everything you need to know about them. - If you are managing a database yourself and it's PostgreSQL, read the configuration chapter in the docs and change the settings accordingly. Defaults aren't optimized for performance.
- DB optimization is a huge topic. If some query is unusually slow, make sure execution plan is correct. PostgreSQL optimizer sometimes messes up and uses hash join-table scan instead of nested loop. Increase stats granularity or force it to use nested loop. Read http://a.co/2GF5Sg8 if you want to know more.
- Avoid N+1, make sure to include related records in queries. In rare cases it's better to use N+1 with Russian doll caching but generally avoiding N+1 is better. There are gems to detect N+1.
- Play with number of processes and threads per process in your app server. Multi-threading helps with I/O bound tasks (querying a slow database, calling remote servers).
- Enable out-of-bound GC in your app server, so that GC won't happen during requests. It has minimal impact but still.
- Off-load long-running tasks (e.g. resizing images and calling external services) to background workers. Use Sidekiq, it's amazing.
- Don't bother with EventMachine, it sucks.
- Make a choice on what to do with JS files. Either use CDN to serve third-party libraries individually or bundle them in a separate file.
- Serve static assets from CDN.
https://www.reddit.com/r/rails/comments/5x0wd2/has_anybody_purchased_the_complete_guide_to_rails/
2017年8月25日 星期五
DATETIME 和 TIMESTAMP 的差別
這邊說的是 database type
幾個結論
1. datetime 存了 date 和 time,可能用到 8 bit
2. datetime 可以支援的 range 比較廣,從 1000~9999 years 都可以
3. timestamp 則是存從 epoch time (1970-01-01) 到現在經過的秒數,只用到 4 bits
4. timestamp 因為存秒數,所以最早只能支援到 epoch time
5. timestamp 只用到 4 bits所以最多到 2038 年
6. rails migration 即使設 timestamp 也是使用 datetime 而不是用 timestamp
From wiki
Because DATETIME stores every digit in the year, month day, hour, minute and second, it uses up a total of 8 bytes. As TIMESTAMP only stores the number of seconds since 1970-01-01, it uses 4 bytes. You can read more about the differences between time formats in MySQL here.
https://stackoverflow.com/questions/3928275/in-ruby-on-rails-whats-the-difference-between-datetime-timestamp-time-and-da
幾個結論
1. datetime 存了 date 和 time,可能用到 8 bit
2. datetime 可以支援的 range 比較廣,從 1000~9999 years 都可以
3. timestamp 則是存從 epoch time (1970-01-01) 到現在經過的秒數,只用到 4 bits
4. timestamp 因為存秒數,所以最早只能支援到 epoch time
5. timestamp 只用到 4 bits所以最多到 2038 年
6. rails migration 即使設 timestamp 也是使用 datetime 而不是用 timestamp
From wiki
Because DATETIME stores every digit in the year, month day, hour, minute and second, it uses up a total of 8 bytes. As TIMESTAMP only stores the number of seconds since 1970-01-01, it uses 4 bytes. You can read more about the differences between time formats in MySQL here.
https://stackoverflow.com/questions/3928275/in-ruby-on-rails-whats-the-difference-between-datetime-timestamp-time-and-da
標籤:
Computer Basic,
Database,
ROR
2017年8月23日 星期三
rails 效能 - group
```
```
app/views/posts/report.html.erb
```
```app/controllers/posts_controller.rb def report - @posts = Post.all.include(:user).sort_by{ |post| post.subscriptions.size }.reverse[0,10] + @posts = Post.includes(:user).joins(:subscriptions).group("posts.id").select("posts.*, COUNT(subscriptions.id) as subscriptions_count").order("subscriptions_count DESC").limit(10) end
```
- <td><%= post.subscriptions.size %></td>
+ <td><%= post.subscriptions_count %></td>
2017年8月17日 星期四
postgresql explain
可以看 sql 的複雜度和 cost,很方便的 sql command
https://www.postgresql.org/docs/9.4/static/using-explain.html
2017年7月22日 星期六
在 rails 內避免數字因為 race condition 而有誤
例如我有個 Account model 內有 balance column,可以這樣做:
```ruby
def increment_balance(amount)
self.class.connection.execute "update accounts set balance = balance + #{amount} where id = #{id}"
add_to_transaction # so after_commit will be triggered
self
end
```
這樣就算我有兩個 account instance 同時存入也沒關係,因為他增加的值是從 database 的值去加的,如果是用
```ruby
account.update(amount: new_amount)
```
這種作法的話就會變成直接 update 成新的值,就可能會有 race condition 的問題
B+ tree (B plus tree)
前一篇學習了 什麼是 B- balance tree,立馬再來補習一下 B+
其實 B+ tree 就是 B- 的升級版
主要的差別在於「子節點有母節點的資訊」,並且「出現在子節點中的母節點元素都是子節點中最大的元素」,不囉唆,看圖:
我們看第三層的所有子節點可以發現,每個子節點最右邊(也就是最大)的元素都是母節點的元素
如此一來就變成一個依照順序排序的子節點
優點:
1. 可以減少 IO 次數,因為子節點有所有的 data,母節點只有索引而已
2. 在做範圍查詢的時候,B- 如果查詢的範圍橫跨節點的兩邊就必須要先走左邊再走右邊去查資料,但 B+ 可以在最底層的子節點往右邊直接找就行了(因為子節點有所有的 data)
3. 更矮胖
4. 所有查詢都要查到子節點,代表查詢的速度比較一致,不會有些快有些慢(穩定性高)(相對的也可以說成是「一樣快」或是「一樣慢」)
圖片以及資訊來源:
http://mp.weixin.qq.com/s/cK_GIhCuGoUwJpDpoaETxw
其實 B+ tree 就是 B- 的升級版
主要的差別在於「子節點有母節點的資訊」,並且「出現在子節點中的母節點元素都是子節點中最大的元素」,不囉唆,看圖:
我們看第三層的所有子節點可以發現,每個子節點最右邊(也就是最大)的元素都是母節點的元素
如此一來就變成一個依照順序排序的子節點
優點:
1. 可以減少 IO 次數,因為子節點有所有的 data,母節點只有索引而已
2. 在做範圍查詢的時候,B- 如果查詢的範圍橫跨節點的兩邊就必須要先走左邊再走右邊去查資料,但 B+ 可以在最底層的子節點往右邊直接找就行了(因為子節點有所有的 data)
3. 更矮胖
4. 所有查詢都要查到子節點,代表查詢的速度比較一致,不會有些快有些慢(穩定性高)(相對的也可以說成是「一樣快」或是「一樣慢」)
圖片以及資訊來源:
http://mp.weixin.qq.com/s/cK_GIhCuGoUwJpDpoaETxw
標籤:
Algorithm,
Computer Basic,
Database
什麼是 btree (balance tree) (b-)
常常看到 postgresql 的 index 都是用 btree 的方式 index,但一直沒時間去研究什麼是 btree,最近發現一個不錯的維信號用漫畫的方式解釋各種演算法相關的東西,剛好看到 b- b+ 的介紹,該是時候學習一下了~
所謂的 b- 其實唸作 balance tree 或是 b tree,是跟二元樹有點相關的東西,但最大的差別在於他每個節點最多可以包容兩個值,這樣做的原因是如果我們用二元樹來下 index,雖然時間複雜度很低,但是由於每個節點都寫入在硬碟的不同位置,一旦運氣不好我們要找的節點剛好是在樹的最底端,那硬碟就要從最上面的節點一路讀取到最下面的節點,雖然時間複雜度低但是硬碟讀取的效率差,為了讓硬碟讀取的次數變少於是有了 b- 的結構。
b- 讓一個節點可以容納兩個數值,如此一來下面就可以有三個節點,並且可以在節點內定位省了一次到不同硬碟空間的時間。
所謂的 b- 其實唸作 balance tree 或是 b tree,是跟二元樹有點相關的東西,但最大的差別在於他每個節點最多可以包容兩個值,這樣做的原因是如果我們用二元樹來下 index,雖然時間複雜度很低,但是由於每個節點都寫入在硬碟的不同位置,一旦運氣不好我們要找的節點剛好是在樹的最底端,那硬碟就要從最上面的節點一路讀取到最下面的節點,雖然時間複雜度低但是硬碟讀取的效率差,為了讓硬碟讀取的次數變少於是有了 b- 的結構。
b- 讓一個節點可以容納兩個數值,如此一來下面就可以有三個節點,並且可以在節點內定位省了一次到不同硬碟空間的時間。
另外新增刪減節點的時候也是比較耗時的,為了確保最有效率地運作,新增刪減節點是有機會去更動到母節點的
總之 postgres 是預設使用 btree,瞭解一下更清楚自己平時在做什麼事XD
另外也寫了一篇 關於 B+ tree的介紹
圖片和資訊來源:
標籤:
Algorithm,
Computer Basic,
Database
2017年4月25日 星期二
About Redis
In memory, key-value store. 所以其實 redis 就是一個 database,只是他所有的東西都存在 memory 而且是屬於 NoSQL 的 database。
最常用的場景就是拿來快取東西。
In memory 表示是直接存在記憶體上不是存在硬碟裡,由於記憶體上有實作更簡單的演算法所以需要的 CPU 資源較少,而且在 query 資料的時候可以減少搜尋硬碟的時間所以讀寫很快,這也是為什麼大家常常拿 Redis 當 Cache server 的原因
https://en.wikipedia.org/wiki/In-memory_database
當然 Redis 也有提供讓資料存寫進硬碟的功能,關鍵字:Expire
http://huli.logdown.com/posts/926762-redis-introduction
http://stackoverflow.com/questions/7888880/what-is-redis-and-what-do-i-use-it-for
延伸閱讀
https://medium.com/ruby-on-rails/easy-caching-with-rails-4-heroku-redis-5fb36381628
最常用的場景就是拿來快取東西。
In memory 表示是直接存在記憶體上不是存在硬碟裡,由於記憶體上有實作更簡單的演算法所以需要的 CPU 資源較少,而且在 query 資料的時候可以減少搜尋硬碟的時間所以讀寫很快,這也是為什麼大家常常拿 Redis 當 Cache server 的原因
https://en.wikipedia.org/wiki/In-memory_database
當然 Redis 也有提供讓資料存寫進硬碟的功能,關鍵字:Expire
http://huli.logdown.com/posts/926762-redis-introduction
http://stackoverflow.com/questions/7888880/what-is-redis-and-what-do-i-use-it-for
延伸閱讀
https://medium.com/ruby-on-rails/easy-caching-with-rails-4-heroku-redis-5fb36381628
標籤:
Computer Basic,
Database
訂閱:
文章 (Atom)


