Select count(*) 的优化
首先說明:
select count(*) 和 select count(1)的效率相差無幾。
這里開始引用自“德哥@Digoal”的博客,原文鏈接:http://blog.163.com/digoal@126/blog/static/163877040201331252945440/
–引用部分開始–
引用塊內(nèi)容
PostgreSQL 的count確實(shí)是一大軟肋, 特別是全表的count.
在9.2以前全表的count只能通過掃描全表來得到, 即使有pk也必須掃描全表.
9.2版本增加了index only scan的功能, count(*)可以通過僅僅掃描pk就可以得到.
但是如果是一個(gè)比較大的表, pk也是很大的, 掃描pk也是個(gè)不小的開銷.
那么有沒有辦法來優(yōu)化count全表的操作呢, 如果你的場景真的有必要頻繁的count全表, 那么可以嘗試一下使用以下方法來優(yōu)化你的場景.
其實(shí)非常簡單, 就是給表建立幾個(gè)觸發(fā)器, 每次插入,刪除,truncate表時(shí)觸發(fā), 將表的記錄數(shù)更新到一個(gè)記錄表中.
但是要知道, 這樣會(huì)帶來一個(gè)問題, 并發(fā)的插入和刪除操作, 如果僅僅使用1條記錄來存儲(chǔ)表的count(*)值的話, 會(huì)有嚴(yán)重的鎖沖突的問題.
例如兩個(gè)session ,同時(shí)插入1條記錄, 在觸發(fā)觸發(fā)器時(shí), 由于都要更新count表的同一條記錄, 那么會(huì)發(fā)生行鎖等待.
因此, 可以使用多條記錄來緩解行鎖沖突的問題, 如下 :
創(chuàng)建測試表, a, 假設(shè)要經(jīng)常count(*) from a.
創(chuàng)建記錄a表記錄數(shù)的表
create table cnt_a(id int primary key, cnt int);為了緩解行鎖沖突, 這里使用了1001條記錄來存儲(chǔ)count(*) from a的值.
insert into cnt_a select generate_series(0,1000),0;在計(jì)算count(*) a時(shí), 使用sum(cnt) from cnt_a就可以了. 因此只需要掃描1001行.
后面會(huì)看到當(dāng)a表的記錄數(shù)越多, 性能提升約明顯.
創(chuàng)建插入/刪除/truncate觸發(fā)器
插入以下記錄后,測試完后通過count(*) 和sum(cnt)比對數(shù)據(jù)是否一致
postgres=# select count(*) from a;count ---------1755964 (1 row) Time: 285.491 ms postgres=# select sum(cnt) from cnt_a ;sum ---------1755964 (1 row) Time: 0.689 ms當(dāng)記錄數(shù)到達(dá)千萬級別后, 性能以及提升幾千倍了.
–引用部分開始–
“德哥@Digoal”又做了一些后續(xù)的優(yōu)化,詳情可點(diǎn)擊鏈接自行移步查看:http://blog.163.com/digoal@126/blog/static/163877040201331252945440/
我在實(shí)際應(yīng)用中也發(fā)現(xiàn)了其中存在死鎖現(xiàn)象,認(rèn)為是觸發(fā)器函數(shù)中select的“Shared Lock”和update的“Exclusive Lock”競爭產(chǎn)生的。詳見我的博客:《Transaction中的SQL死鎖》http://blog.csdn.net/fm0517/article/details/52242285
于是我也做了一些改進(jìn),即:假設(shè)表cnt_a中的記錄數(shù)是固定的,所以在insert觸發(fā)器和delete觸發(fā)器中,不再從cnt_a中去取max(id),從而避免了死鎖。
改進(jìn)的觸發(fā)器函數(shù):
最后值得注意的是:該方法雖然在大數(shù)據(jù)量的情況下能夠大幅提高select count(*)的效率,但是增加了insert和delete時(shí)數(shù)據(jù)庫的負(fù)擔(dān)。所以使用時(shí)要謹(jǐn)慎綜合考慮實(shí)際情況再做決定。
總結(jié)
以上是生活随笔為你收集整理的Select count(*) 的优化的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: select max(id)优化
- 下一篇: Transaction中的SQL死锁