日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

Select count(*) 的优化

發布時間:2025/5/22 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Select count(*) 的优化 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

首先說明:
select count(*) 和 select count(1)的效率相差無幾。

這里開始引用自“德哥@Digoal”的博客,原文鏈接:http://blog.163.com/digoal@126/blog/static/163877040201331252945440/

–引用部分開始–

引用塊內容
PostgreSQL 的count確實是一大軟肋, 特別是全表的count.
在9.2以前全表的count只能通過掃描全表來得到, 即使有pk也必須掃描全表.
9.2版本增加了index only scan的功能, count(*)可以通過僅僅掃描pk就可以得到.
但是如果是一個比較大的表, pk也是很大的, 掃描pk也是個不小的開銷.
那么有沒有辦法來優化count全表的操作呢, 如果你的場景真的有必要頻繁的count全表, 那么可以嘗試一下使用以下方法來優化你的場景.
其實非常簡單, 就是給表建立幾個觸發器, 每次插入,刪除,truncate表時觸發, 將表的記錄數更新到一個記錄表中.
但是要知道, 這樣會帶來一個問題, 并發的插入和刪除操作, 如果僅僅使用1條記錄來存儲表的count(*)值的話, 會有嚴重的鎖沖突的問題.
例如兩個session ,同時插入1條記錄, 在觸發觸發器時, 由于都要更新count表的同一條記錄, 那么會發生行鎖等待.
因此, 可以使用多條記錄來緩解行鎖沖突的問題, 如下 :
創建測試表, a, 假設要經常count(*) from a.

create table a(id serial4 primary key, info text, crt_time timestamp(0) default now());

創建記錄a表記錄數的表

create table cnt_a(id int primary key, cnt int);

為了緩解行鎖沖突, 這里使用了1001條記錄來存儲count(*) from a的值.

insert into cnt_a select generate_series(0,1000),0;

在計算count(*) a時, 使用sum(cnt) from cnt_a就可以了. 因此只需要掃描1001行.
后面會看到當a表的記錄數越多, 性能提升約明顯.
創建插入/刪除/truncate觸發器

CREATE OR REPLACE FUNCTION public.tg_insert_a()RETURNS triggerLANGUAGE plpgsql AS $function$ declarem_id int;rm numeric; beginselect max(id),random() into m_id,rm from cnt_a;update cnt_a set cnt=cnt+1 where id=(rm*m_id)::int;return null; end; $function$;CREATE OR REPLACE FUNCTION public.tg_delete_a()RETURNS triggerLANGUAGE plpgsql AS $function$ declarem_id int;rm numeric; beginselect max(id),random() into m_id,rm from cnt_a;update cnt_a set cnt=cnt-1 where id=(rm*m_id)::int;return null; end; $function$;CREATE OR REPLACE FUNCTION public.tg_truncate_a()RETURNS triggerLANGUAGE plpgsql AS $function$ declare beginupdate cnt_a set cnt=0 where not cnt=0;return null; end; $function$;create trigger tg1 after insert on a for each row execute procedure tg_insert_a(); create trigger tg2 after delete on a for each row execute procedure tg_delete_a(); create trigger tg3 after truncate on a for each statement execute procedure tg_truncate_a();

插入以下記錄后,測試完后通過count(*) 和sum(cnt)比對數據是否一致

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

當記錄數到達千萬級別后, 性能以及提升幾千倍了.

–引用部分開始–

“德哥@Digoal”又做了一些后續的優化,詳情可點擊鏈接自行移步查看:http://blog.163.com/digoal@126/blog/static/163877040201331252945440/

我在實際應用中也發現了其中存在死鎖現象,認為是觸發器函數中select的“Shared Lock”和update的“Exclusive Lock”競爭產生的。詳見我的博客:《Transaction中的SQL死鎖》http://blog.csdn.net/fm0517/article/details/52242285

于是我也做了一些改進,即:假設表cnt_a中的記錄數是固定的,所以在insert觸發器和delete觸發器中,不再從cnt_a中去取max(id),從而避免了死鎖。
改進的觸發器函數:

CREATE OR REPLACE FUNCTION tg_insert_a()RETURNS triggerLANGUAGE plpgsql AS $function$ declarerm numeric; beginselect random() into rm;update trigger_cnt set cnt=cnt+1 where id=(rm*1000)::int;return null; end; $function$;CREATE OR REPLACE FUNCTION tg_delete_a()RETURNS triggerLANGUAGE plpgsql AS $function$ declarerm numeric; beginselect random() into rm;update trigger_cnt set cnt=cnt-1 where id=(rm*1000)::int;return null; end; $function$;

最后值得注意的是:該方法雖然在大數據量的情況下能夠大幅提高select count(*)的效率,但是增加了insert和delete時數據庫的負擔。所以使用時要謹慎綜合考慮實際情況再做決定。

總結

以上是生活随笔為你收集整理的Select count(*) 的优化的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。