顯示具有 Database 標籤的文章。 顯示所有文章
顯示具有 Database 標籤的文章。 顯示所有文章

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日 星期四

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 不適用




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` 就解決了

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


2017年10月10日 星期二

reddit 上的 rails performance 總結

The Complete Guide to Rails Performance 這本書 https://www.railsspeed.com/ 的總結:

  • 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 by name. 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

2017年8月23日 星期三

rails 效能 - group

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

```
app/views/posts/report.html.erb
-  <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


什麼是 btree (balance tree) (b-)

常常看到 postgresql 的 index 都是用 btree 的方式 index,但一直沒時間去研究什麼是 btree,最近發現一個不錯的維信號用漫畫的方式解釋各種演算法相關的東西,剛好看到 b- b+ 的介紹,該是時候學習一下了~


所謂的 b- 其實唸作 balance tree 或是 b tree,是跟二元樹有點相關的東西,但最大的差別在於他每個節點最多可以包容兩個值,這樣做的原因是如果我們用二元樹來下 index,雖然時間複雜度很低,但是由於每個節點都寫入在硬碟的不同位置,一旦運氣不好我們要找的節點剛好是在樹的最底端,那硬碟就要從最上面的節點一路讀取到最下面的節點,雖然時間複雜度低但是硬碟讀取的效率差,為了讓硬碟讀取的次數變少於是有了 b- 的結構。

b- 讓一個節點可以容納兩個數值,如此一來下面就可以有三個節點,並且可以在節點內定位省了一次到不同硬碟空間的時間。






另外新增刪減節點的時候也是比較耗時的,為了確保最有效率地運作,新增刪減節點是有機會去更動到母節點的



總之 postgres 是預設使用 btree,瞭解一下更清楚自己平時在做什麼事XD

另外也寫了一篇 關於 B+ tree的介紹


圖片和資訊來源:

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