Trở lại năm 2015, tôi đã nâng cấp cơ sở dữ liệu Oracle 11.2.0.4 của mình lên 12.1.0.2 và gặp phải một số vấn đề về hiệu suất liên quan đến việc sử dụng GTT của chúng tôi. Tôi đã viết blog về những vấn đề đó ở đây.
Điểm mấu chốt của vấn đề mà tôi đang cố gắng giải quyết là sự thay đổi hành vi trong 12c dẫn đến việc lưu số liệu thống kê của Oracle rằng GTT không có hàng nào khi nó không có. Thống kê hiển thị số hàng bằng 0 dẫn đến quét toàn bộ bảng và các sản phẩm theo nhóm trên các truy vấn liên quan đến GTT. Như tôi đã nêu trong bài đăng trên blog đó, chúng tôi đã sử dụng DBMS_STATS.SET_TABLE_STATS sau khi chúng tôi điền vào bảng với dữ liệu để mỗi phiên sẽ có số liệu thống kê thích hợp để đi đến một kế hoạch thực thi tốt hơn.
Sau khi chúng tôi nâng cấp lên Oracle 19c, chúng tôi bắt đầu thấy các vấn đề hiệu suất khác liên quan đến GTT. Các truy vấn đã sử dụng GTT bắt đầu chờ sự kiện chờ "ghim con trỏ:S chờ trên X". Đây có thể là một sự thay đổi hành vi với phiên bản Oracle mới, nhưng cũng có thể là do các nhà phát triển của chúng tôi sử dụng GTT trong mã của chúng tôi thường xuyên hơn và không liên quan gì đến phiên bản mới.
Đối với các truy vấn liên quan đến sự kiện chờ Ghim con trỏ, tôi nhận thấy một số lượng lớn các phiên bản của câu lệnh SQL trong Nhóm chia sẻ. Khi tôi truy vấn V $ SQL_SHARED_CURSOR, tôi phát hiện ra rằng PURGED_CURSOR =’Y’ cho các câu lệnh SQL này. Con trỏ đang trở nên vô hiệu.
Khi nghiên cứu vấn đề này, tôi phát hiện ra rằng điều xảy ra là mỗi khi chúng tôi gọi DBMS_STATS.SET_TABLE_STATS để nhận thống kê dựa trên phiên trên GTT, nó sẽ làm mất hiệu lực tất cả các câu lệnh SQL sử dụng GTT đó. Do đó, sự chờ đợi. Việc chờ đợi không kéo dài nên nhiều người dùng cuối thậm chí không nhận thấy vấn đề.
Nhưng sau đó chúng tôi gặp một vấn đề mới. Khi bạn thực hiện cuộc gọi tới SET_TABLE_STATS, Oracle ghi một mục nhập vào SYS.WRI $ _OPTSTAT_TAB_HISTORY và bạn có thể thấy các giá trị mà phiên đã đặt cho thống kê của bảng. Theo mặc định, bảng này lưu trữ lịch sử 30 ngày. Bảng này đang phát triển rất mạnh và tiêu thụ phần lớn SYSAUX. Thường xuyên (hàng giờ?) Oracle sẽ xóa các mục nhập hơn 30 ngày tuổi. Việc cắt tỉa bảng này thường xuyên hiện đang ảnh hưởng tiêu cực đến hiệu suất của người dùng cuối. Sau đây là biểu đồ hiệu suất từ Lighty cho thấy tác động của việc cắt bớt bảng này:
Tất cả màu đỏ đáng sợ đó là khi các hàng cũ bị xóa khỏi SYS.WRI $ _OPTSTAT_TAB_HISTORY.
Vì vậy, "sửa chữa" hiệu suất của tôi năm năm trước đã giới thiệu một vấn đề hiệu suất khác. Để cải thiện hiệu suất, những gì tôi đã làm là tạo số liệu thống kê được chia sẻ trên GTT và ngừng sử dụng số liệu thống kê phiên. Đây là các bước:
--set prefs to SHARED globally
exec DBMS_STATS.set_global_prefs ( pname => 'GLOBAL_TEMP_TABLE_STATS', pvalue => 'SHARED');
--set the table and index stats
exec dbms_stats.set_table_stats(ownname=>'MY_SCHEMA',tabname=>'MY_GTT_TABLE',numrows=>1000,numblks=>2,avgrlen=>15);
exec dbms_stats.set_index_stats(ownname=>'MY_SCHEMA',indname=>'GTT_INDEX',indlevel=>1,numlblks=>2,numdist=>15,clstfct=>28,numrows=>1000);
-- set prefs back to SESSION
exec DBMS_STATS.set_global_prefs ( pname => 'GLOBAL_TEMP_TABLE_STATS', pvalue => 'SESSION');
-- verify stats set
select num_rows,blocks,last_analyzed,scope
from dba_tab_statistics
where table_name ='MY_GTT_TABLE';
select blevel,leaf_blocks,distinct_keys,num_rows,clustering_factor,last_analyzed,scope
from dba_ind_statistics
where index_name='GTT_INDEX' and owner='MY_SCHEMA';
Khi thống kê được chia sẻ đã có, chúng tôi xóa các lệnh gọi tới DBMS_SET_TABLE_STATS khỏi mã của chúng tôi.