8288分类目录 8288分类目录 8288分类目录
  当前位置:海洋目录网 » 站长资讯 » 站长资讯 » 文章详细 订阅RssFeed

Oracle结果集缓存(Result Cache)--服务器、客户端、函数缓存

来源:本站原创 浏览:82次 时间:2022-01-24
Oracle结果集缓存(Result Cache)--服务器、客户端、函数缓存


在11g中,Oracle提供了结果集缓存特性。该缓存是在共享内存中存储全部的结果集,如果一个查询SQL被执行,且它对应的结果集在缓存中,那么,该SQL的几乎全部开销都可以避免。这些开销包括,解析时间、逻辑读、物理读和任意的通常可能会遭遇的争用。但是,在实际的情况中,结果集缓存仅在少数的情况下是有效的,原因有如下几点:  

(1)有数据重叠的多个SQL会在缓存中保存冗余的数据。  

(2)对依赖对象的任何改变(包括对查询中引用的任意表)都会使整个缓存的结果集变为无效,结果集缓存最适合那些只读或接近只读的表。  

(3)大的结果集要么是因为结果集缓存太少不能放入,要么会强迫缓存中所有已存在的条件换出。为了防止这种情况发生,我们必须限制结果集缓存仅用于较小的结果集。  

(4)在缓存中快速并发的结果集的创建可能会导致结果集闩锁的争用。因此,结果集缓存的内容必须变得相对慢一些。


结果集缓存由如下一些参数控制:  

result_cache_mode :该参数用来控制结果集缓存的操作模式。OFF表示禁用,MANUAL表示只有使用了result_cahce提示的查询或对带有result_cache(mode force)属性的表访问的查询才会被缓存。FORCE表示所有合适的查询都会被缓存。  

result_cache_max_size:控制结果集缓存的大小,默认是共享池的1%。  

result_cache_max_result:单个结果集能够消耗的缓存的最大百分比。比这个值大的,将不能被缓存。  

下面,我们来进行一个配置:  

SQL> alter session set result_cache_mode = manual

  2  /


Session altered.


SQL> show parameters result_cache_max


NAME                                 TYPE

------------------------------------ ----------------------

VALUE

------------------------------

result_cache_max_result              integer

5

result_cache_max_size                big integer

1568K

SQL>

下面,我们来运行一个结果集缓存的查询:  

SQL> create table t

  2  as

  3  select * from all_objects

  4  /


Table created.


SQL> alter table t add constraint t_pk primary key(object_id)

  2  /


Table altered.


SQL> exec dbms_stats.gather_table_stats(user,'t',cascade=>true)


PL/SQL procedure successfully completed.


SQL> set autotrace traceonly

SQL> select /*+result_cache*/ *

  2  from t

  3  where object_id <= 300

  4  /



Execution Plan

----------------------------------------------------------

Plan hash value: 3772518221


-------------------------------------------------------------------------------- ---------------------------

| Id  | Operation                    | Name                       | Rows  | Byte s | Cost (%CPU)| Time     |

-------------------------------------------------------------------------------- ---------------------------

|   0 | SELECT STATEMENT             |                            |   100 |  860 0 |     4   (0)| 00:00:01 |

|   1 |   RESULT CACHE                | c588m2d8c1u4f4qb361bw3h58y |        |   |            |          |

|   2 |   TABLE ACCESS BY INDEX ROWID| T                          |   100 |  860 0 |     4   (0)| 00:00:01 |

|*  3 |    INDEX RANGE SCAN          | T_PK                       |   100 |   |     2   (0)| 00:00:01 |

-------------------------------------------------------------------------------- ---------------------------


Predicate Information (identified by operation id):

---------------------------------------------------


   3 - access("OBJECT_ID"<=300)


Result Cache Information (identified by operation id):

------------------------------------------------------


   1 - column-count=15; dependencies=(DJP01.T); attributes=(ordered); 

        name="select /*+result_cache*/ *

                     from t

                   where object_id <= 300"


Statistics

----------------------------------------------------------

           1  recursive calls

          0  db block gets

           5  consistent gets

          0  physical reads

          0  redo size

       1649  bytes sent via SQL*Net to client

        419  bytes received via SQL*Net from client

          2  SQL*Net roundtrips to/from client

          0  sorts (memory)

          0  sorts (disk)

          5  rows processed


SQL> set autotrace off

SQL>

说明:当使用结果集缓存的时候,会在执行计划中看到上述的黑体部分的信息。其中,name列中的值,是结果集缓存在内存当中的一个地址。这时,上述对应的查询结果已经被缓存。  

下面,我们对其再次进行一个访问:  

SQL> set autotrace traceonly

SQL> select *

  2  from t

  3  where object_id <= 300

  4  /



Execution Plan

----------------------------------------------------------

Plan hash value: 3772518221


-------------------------------------------------------------------------------- ----

| Id  | Operation                   | Name | Rows  | Bytes | Cost (%CPU)| Time    |

-------------------------------------------------------------------------------- ----

|   0 | SELECT STATEMENT            |      |   100 |  8600 |     4   (0)| 00:00: 01 |

|   1 |  TABLE ACCESS BY INDEX ROWID| T    |   100 |  8600 |     4   (0)| 00:00: 01 |

|*  2 |   INDEX RANGE SCAN          | T_PK |   100 |       |     2   (0)| 00:00: 01 |

-------------------------------------------------------------------------------- ----


Predicate Information (identified by operation id):

---------------------------------------------------


   2 - access("OBJECT_ID"<=300)



Statistics

----------------------------------------------------------

           1  recursive calls

          0  db block gets

           5  consistent gets

          0  physical reads

          0  redo size

       1770  bytes sent via SQL*Net to client

        419  bytes received via SQL*Net from client

          2  SQL*Net roundtrips to/from client

          0  sorts (memory)

          0  sorts (disk)

          5  rows processed


SQL> select /*+result_cache*/ *

  2  from t

  3  where object_id <= 300

  4  /



Execution Plan

----------------------------------------------------------

Plan hash value: 3772518221


-------------------------------------------------------------------------------- ---------------------------

| Id  | Operation                    | Name                       | Rows  | Byte s | Cost (%CPU)| Time     |

-------------------------------------------------------------------------------- ---------------------------

|   0 | SELECT STATEMENT             |                            |   100 |  860 0 |     4   (0)| 00:00:01 |

|   1 |  RESULT CACHE                | c588m2d8c1u4f4qb361bw3h58y |       |   |            |          |

|   2 |   TABLE ACCESS BY INDEX ROWID| T                          |   100 |  860 0 |     4   (0)| 00:00:01 |

|*  3 |    INDEX RANGE SCAN          | T_PK                       |   100 |   |     2   (0)| 00:00:01 |

-------------------------------------------------------------------------------- ---------------------------


Predicate Information (identified by operation id):

---------------------------------------------------


   3 - access("OBJECT_ID"<=300)


Result Cache Information (identified by operation id):

------------------------------------------------------


   1 - column-count=15; dependencies=(DJP01.T); attributes=(ordered); name="sele

ct /*+result_cache*/ *

from t

where object_id <= 300"



Statistics

----------------------------------------------------------

           0  recursive calls

          0  db block gets

           0  consistent gets

          0  physical reads

          0  redo size

       1649  bytes sent via SQL*Net to client

        419  bytes received via SQL*Net from client

          2  SQL*Net roundtrips to/from client

          0  sorts (memory)

          0  sorts (disk)

          5  rows processed


SQL> set autotrace off

SQL>

说明;在第一次使用result_cache提示进行查询时,对结果集进行了缓存。接一下来的查询当中,我分别使用了无result_cache提示和有result_cache提示的查询进行了对比。对比发现,无提示的SQL仍然进行了SQL的相关处理(比如,解析、逻辑读取等),尽管我们对结果集缓存了亦是如此。这是因为该查询中没有使用result_cache提示,Oracle无法识别该查询对应的结果集是否被缓存,因而使用了常规SQL流程。只胡对结果集进行了缓存,而且必须是完全相同的SQL(比如第一次查询使用了提示,第二次查询她必须使用),这样,才能利用结果集缓存中的数据。如上述最后一个查询所示。  


我们可以使用v$result_cache_statistics视图来查看结果集缓存的统计数据,SQL如下:  

SQL> column name format a30

SQL> column value format a10

SQL> select name,value

  2  from v$result_cache_statistics

  3  /


NAME                           VALUE

------------------------------ ----------

Block Size (Bytes)             1024

Block Count Maximum            1568

Block Count Current            32

Result Size Maximum (Blocks)   78

Create Count Success           1

Create Count Failure           0

Find Count                     1

Invalidation Count             0

Delete Count Invalid           0

Delete Count Valid             0

Hash Chain Length              1


11 rows selected.


SQL>

说明:Create Count Success表示创建成功的结果集缓存的个数。Find Count 表示在结果集缓存中发现查询的个数。Invalidation Count表示当DML改变一个依赖对象的内容的时,无效的结果集缓存的个数。Delete Count Invalid表示为了给新的结果集腾出空间,从内存中删除无效结果集的个数。  


在实际当中,只有少部分结果集是有效的,而且大部分是比由那些没有被使用就被换出内存的结果集组成。我们可以通过查询执行计划中包含result cache操作且在缓存里的那些SQL的统计数据,我们还可以检查单个结果集的效率。SQL如下:  

SQL> with result_cache

  2  as(

  3  select cache_id,count(*) cache_sets,sum(scan_count) rc_hits

  4  from v$result_cache_objects

  5  group by cache_id

  6  )

  7  select /*+ordered*/ s.sql_id,s.executions,o.cache_sets,o.rc_hits,

  8         round(s.rows_processed/executions) avg_rows,buffer_gets,

  9         round(buffer_gets/(executions-o.rc_hits)) avg_gets_nocache,

 10         round(buffer_gets/(executions-o.rc_hits)*o.rc_hits) estd_saved_gets,

 11         s.sql_text

 12  from v$sql_plan p join result_cache o on (p.object_name = o.cache_id)

 13                    join v$sql s on (s.sql_id = p.sql_id and s.child_number = p.child_number)

 14  where operation = upper('result cache')

 15  order by 7 desc

 16  /


SQL_ID                     EXECUTIONS CACHE_SETS    RC_HITS   AVG_ROWS

-------------------------- ---------- ---------- ---------- ----------

BUFFER_GETS AVG_GETS_NOCACHE ESTD_SAVED_GETS

----------- ---------------- ---------------

SQL_TEXT

--------------------------------------------------------------------------------

6735aa1xanfym                       2          1          1          5

          5                5               5

select /*+result_cache*/ * from t where object_id <= 300



SQL>


当DML语句修改了查询涉及的任意一个依赖的表时,缓存的结果集会被从缓存中消除。我们可以用如下的SQL来查看缓存中的查询和依赖的对象,SQL如下:  

SQL> select /*+ordered*/ max(o2.name) cache_object,count(*) sets_cached,

  2        o1.cache_id dependency

  3  from v$result_cache_dependency d join v$result_cache_objects o1

  4       on ( d.depend_id = o1.id) join v$result_cache_objects o2

  5       on (d.result_id = o2.id)

  6  group by o1.cache_id

  7  /


CACHE_OBJECT

--------------------------------------------------------------------------------

SETS_CACHED

-----------

DEPENDENCY

--------------------------------------------------------------------------------

select /*+result_cache*/ *

from t

where object_id <= 300

          1

DJP01.T



SQL>





众所周知,内存的访问速度远远高于磁盘,把经常使用的数据存放于内存中来提高检索速度已不是什么新鲜事了。Cache 是内存中的一片区域,也叫缓存,如今关系型数据库正是利用这一种重要机制,使得访问数据库的性能突飞猛进。

Result Cache简述

数据库在构建一个SQL查询结果时,不仅需要在访问数据上消耗时间,数据的排序、聚合以及连接操作也会消耗一部分时间。对此Oracle 在11g R1版本中引入了Result Cache(结果集缓存),目的就是将查询结果直接放入内存中,节省构建结果集所花费的时间和资源。引擎可以直接返回结果,而不必在缓存中读取数据.

我们可以把SQL执行概括为三个阶段:

1.从存储获取数据到内存(对于尚未在缓存中的数据)

2.对缓存数据集进行筛选

3.返回结果集给客户端

Result Cache在启用的情况下,不仅会把结果集返回给客户端,而且还会把结果集缓存在Shared Pool特定的一块区域内。当任何客户端执行类似SQL返回相同结果时,Oracle会跳过1和2直接从Result Cache中返回结果,大大提高了性能。

Result Cache可以分为:Server Result Cache(服务器端结果集缓存) 和 Client Result Cache (客户端结果集缓存)

Server Result Cache,通过在Shared Pool中单独分配一块内存来进行结果缓存。

Client Result Cache,通过应用程序层配置,缓存结果集位于客户端内存中。并可以在所有Session间共享,当查询被反复执行时,查询结果可以直接从客户端的缓存中获得,从而极大地提高了应用效率。

Result Cache 特性可以通过一些动态参数进行控制,比如用以定义结果集的内存池的大小、触发的方式等。

默认情况下Result Cache是开启的,所有开发人员都可以使用该特性。

1.表级别使用

2.语句级别使用

默认情况下,如果基础表发生改变,Oracle会将Result Cache中的结果集设置为无效,使得客户端不会从结果缓存中获取过时的数据。当查询运行时,失效的Result Cache结果集将重新被缓存,然后在提交DML发生数据修改时再次被设置为无效。

Result Cache使用监控(相关查看包及视图)

v$result_cache_statistics 内存统计数据

v$result_cache_object 对象跟属性

v$result_cache_dependency 显示结果的依赖关系

v$result_cache_memory 显示内存块及统计数据

Result Cache的影响

Result Cache的读取和修改与其他Oracle部分内存池的读取修改情况类似,也是通过Latch锁进行保护,读取缓存池时需要持有共享锁,修改时(用于添加新的结果集或使现有结果集无效)需要持有排他锁,以避免其他会话同时访问。

因此频繁的更新Result Cache中对应的表格会引起Latch锁的争用(特别是RC Latch)

如果在某些对象上几乎所有查询都同时并发使用Result Cache,情况会更糟。

下面以一个具体的案例介绍:

某客户从AIX平台迁移到x86稳定运行半个月后,高峰期时常出现卡顿,提取故障点的awr及ash报告。从上述等待事件可以看出数据库大部分时间都花在enq: TX – row lock contention和latch free上。

第一反应可能觉得是enq: TX – row lock contention引起的故障,但随后进一步沟通发现,从迁移之后程序代码并无变化。既然业务逻辑没有改变TX锁引起此次故障可能性不大,初步判定是由于latch free 使得TX锁加剧。继续查看latch free 相关的信息。

latch的信息统计,定位主要元凶是Result Cache:RC Latch。

查看result相关参数其中result_cache_mode为force,解决办法可以直接把result_cache_mode改成manual或直接禁用result cache 设置result_cache_max_size为0。

结论

Result Cache是Oracle又一个进步,能大幅提升性能,但也像其他特性一样,不是所有的会话都适用。






一、Result Set Caching 说明


1、概述:Oracle 从11g开始引入了结果集缓存(result cache)的新特性,用于存储经常使用的SQL语句和函数的查询结果,将来语句再执行的时候,oracle直接的访问内存得到结果

2、优点:重用相同的结果集,减少逻辑IO,提高系统性能

3、分类:oracle数据库引擎提供了三种结果集缓存,包括:服务器查询结果集缓存、pl/sql函数结果集缓存和客户端结果集缓存

4、应用场合:访问多行返回少数行的语句,非常适合只读、读>>写、典型OLTP等系统的功能;ORACLE建议应用仅仅对read only的表中使用result cache缓存功能

5、影响因素:在SGA中缓存sql语句的执行结果,相同sql再次执行时,直接从SGA直接读结果就可以了,不需要在去数据库中扫描索引,或是回表,计算之类的,
                          若sql中对应的对象(比如表)做了update,insert,delete或是ddl操作,相关所有sql的缓存结果集就自动失效了,重新刷新结果集缓存
                          result cache也有类似enqueue/lock的保护机制,RC enqueue就是拿来保护并发修改的。result cache他依赖是object level的,
                          既不是row level的,也不是block level的。任何DML/DDL(甚至包括grant)都会使基于这个object的result cache变为invalidate。
                          所以result cache只有对那些在平时几乎没有任何DML的只读表比较有用,可以减轻io的压力。
                          在平时读取阶段不是使用的shared pool latch,而是使用的result cache latch

6、使用限制: 

             (1)查询使用非确定性的函数,序列和临时表的结果集不会被缓存

             (2)查询违反了读一致性时结果集将不会被缓存
             (3)引用数据字典视图的查询的结果集不会被缓存
             (4)查询结果集大于可用缓存结果集可用空间的不被缓存;采用LRU算法来管理result cache。

二、服务器端结果集缓存


1、 相关参数说明

影响result cache开启使用的两个参数:result_cache_mode和result_cache_max_size

RESULT_CACHE_MODE 

参数有三个值:AUTO、MANUAL 和FORCE
(1) 设置为AUTO 时,优化程序将根据重复的执行操作确定将哪些结果存储在高速缓存中。
(2) 设置为MANUAL(默认值)时,必须使用RESULT_CACHE (v$sql_hint视图查看hint信息) 提示指定在高速缓存中存储特定结果。
(3) 设置为FORCE 时,所有结果都将存储在高速缓存中。

注:对于AUTO 和FORCE 设置,如果语句中包含[NO_]RESULT_CACHE 提示,则该提示优先于参数设置。

RESULT_CACHE_MAX_SIZE 

参数设置分配给结果高速缓存的内存,如果将其值设为0,则会禁用结果高速缓存
默认值取决于其它三个内存参数设置(memory_target的0.25% 或sga_target 的0.5% 或shared_pool_size 的1%),其值为32K的整数倍; 
官档提示:Oracle Database will not allocate more than 75% of the shared pool to the server result cache
实验结果:最大值不会大于三个内存参数设置的80%(按shared_pool_size、sga_target、memory_target依次优先检查设置的值)。
即:当三个都设置了时,以shared_pool_size为准;shared_pool_size没设置时,以sga_target为准;只有memory_target设置时,以memory_target为准。

注意:无论默认取值还是最大取值,约定为大于等于指定值的最小32K倍数值;如当shared_pool_size=100K时,则80%为80K,80K不是32K整数倍,大于80K的32K倍数值最小值为96K;
重置shared_pool_size或shared_pool_size为0时,并不认为没有设置,此时RESULT_CACHE_MAX_SIZE始终为0(由于最大值是0的80%的缘故);当修改过这三个值0时,要恢复不设置
状态,可以生成pfile,从pfile中删除对应参数记录

计算公式参数值换算成KB单位:
--默认值
shared_pool_size(GB):SELECT CEIL(shared_pool_size * 0.01 / 32)     * 32||'K' AS "RESULT_CACHE_MAX_SIZE" FROM dual;
      sga_target(GB):SELECT CEIL(sga_target * 0.01 * 0.5 / 32)     * 32||'K' AS "RESULT_CACHE_MAX_SIZE" FROM dual;
   memory_target(GB):SELECT CEIL(memory_target * 0.01 * 0.25 / 32) * 32||'K' AS "RESULT_CACHE_MAX_SIZE" FROM dual;

--最大值
SELECT 'result_cache_max_size' as "NAME", CEIL(para_size * 0.8 / 32) * 32 / 1024 ||'K' AS "DISPLAY_VALUE" FROM dual;

新装数据库根据选择ASSM还是AMM觉定(memory_target和shared_pool_size有一值为0,一值为非0值)。

例如:

--选择ASMM
col name for a30
col display_value for a30
select name,display_value from v$parameter where name in ('memory_target','shared_pool_size','sga_target', 'result_cache_max_size');

NAME                           DISPLAY_VALUE
------------------------------ ------------------------------
shared_pool_size               0
sga_target                     3G
memory_target                  0
result_cache_max_size          15744K

col name for a30
col display_value for a30
SELECT 'result_cache_max_size' as "NAME", CEIL(3 * 1024 * 1024 * 0.01 * 0.5 / 32)     * 32||'K' AS "DISPLAY_VALUE" FROM dual;

NAME                           DISPLAY_VALUE
------------------------------ ------------------------------
result_cache_max_size          15744K

--选择AMM

col name for a30
col display_value for a30
select name,display_value from v$parameter where name in ('memory_target','shared_pool_size','sga_target', 'result_cache_max_size');

NAME                           DISPLAY_VALUE
------------------------------ ------------------------------
shared_pool_size               0
sga_target                     0
memory_target                  4G
result_cache_max_size          10496K

col name for a30
col display_value for a30
SELECT 'result_cache_max_size' as "NAME", CEIL(4 * 1024 * 1024 * 0.01 * 0.25 / 32)     * 32||'K' AS "DISPLAY_VALUE" FROM dual;

NAME                           DISPLAY_VALUE
------------------------------ ------------------------------
result_cache_max_size          10496K

--选择MSMM
col name for a30
col display_value for a30
select name,display_value from v$parameter where name in ('memory_target','shared_pool_size','sga_target', 'result_cache_max_size');

NAME                           DISPLAY_VALUE
------------------------------ ------------------------------
shared_pool_size               480M
sga_target                     0
memory_target                  0
result_cache_max_size          4928K

col name for a30
col display_value for a30
SELECT 'result_cache_max_size' as "NAME", CEIL(480 * 1024 * 0.01 / 32)     * 32||'K' AS "DISPLAY_VALUE" FROM dual;

NAME                           DISPLAY_VALUE
------------------------------ ------------------------------
result_cache_max_size          4928K

SQL> ALTER SYSTEM SET result_cache_max_size=1g;

System altered.

SQL> select name,display_value from v$parameter where name in ('memory_target','shared_pool_size','sga_target', 'result_cache_max_size');


NAME                           DISPLAY_VALUE
------------------------------ ------------------------------
shared_pool_size               480M
sga_target                     0
memory_target                  0
result_cache_max_size          384M

SQL> select 384/480 from dual;

   384/480
----------
        .8

小结:由上可知,RESULT_CACHE_MAX_SIZE三种默认取值恰好对应的是数据库内存的三种管理模式(AMM、ASMM和MSMM);手动设置时,最大值为shared_pool_size的80%

RESULT_CACHE_MAX_RESULT

限制单个result所能占据query cache的最大百分比大小;默认是5%

RESULT_CACHE_REMOTE_EXPIRATION

设置远程数据库结果集缓存过期的时间,以分钟为单位,默认值为0不缓存远程数据库结果集

2、相关视图说明

V$RESULT_CACHE_STATISTICS:列出各种缓存设置和内存使用统计数据。
    V$RESULT_CACHE_MEMORY:列出所有的内存块和相应的统计信息。
   V$RESULT_CACHE_OBJECTS:列出所有的对象(缓存的结果和依赖的对象)和它们的属性。
V$RESULT_CACHE_DEPENDENCY:列出缓存的结果和依赖对象间的依赖详情。

3、相关的包:DBMS_RESULT_CACHE
3.1 STATUS存储过程
--DISABLED:表示没有开启
--ENABLE  :表示已经开启了,并且可以使用结果集缓存
--BYPASS  :表是已经开启了,但不可以使用结果集缓存(跳过了),此时可以通过执行 exec dbms_result_cache.bypass(FALSE);
-----------如果仍然是bypass则可能是参数result_cache_max_size 的值为0的原因
--SYNC    :结果缓存是可用的,但是目前正与其他RAC节点重新同步。

3.2 MEMORY_REPORT存储过程    :列出结果缓存内存利用的一个概要(默认)或详细的报表。
 
3.3 FLUSH存储过程            :清空整个结果缓存的内容。
 
3.4 INVALIDATE存储过程       :使结果缓存中一个特定对象的缓存结果无效。
 
3.5 INVALIDATE_OBJECT存储过程:根据缓存ID使一特定结果缓存无效。

注意: 11g的active dataguard的备库是不能使用result cache的,这是oracle的一个bug,因为涉及到内码的问题,所以oracle一直没有修复,
      result cache目前可以使用在单节点主库和rac环境。

开启result cache
1、设置RESULT_CACHE_MODE为FORCE
2、根据内存管理方式调整相关参数,设置RESULT_CACHE_MAX_SIZE大小
3、检查result cache是否开启:select dbms_result_cache.status from dual;
 
关于设置result cache遇到的问题:

场景: 安装库时选择的是ASMM模式,后来ASMM调整成了AMM,设置sga_target=0;导致设置result_cache_max_size时始终为0设置不了其他值

原因: ASMM时,result_cache_max_size以sga_target参数为参考值,当设置sga_target=0时,由于sga_target还存在于spfile中;使得result_cache_max_size仍以sga_target参数为参考值,
      由最大值限制,result_cache_max_size时始终为0

解决: 执行alter system reset sga_target;重启库设置result_cache_max_size值;这样使得sga_target从spfile中删除;从而result_cache_max_size以memory_target参数为参考值

扩展:result_cache_max_size从spfile中shared_pool_size、sga_target和memory_target依次查找选择参考值

查找参数是否在spfile中(isspecified为TRUE表示指定在spfile中):
SELECT NAME, VALUE, display_value, isspecified
  FROM v$spparameter
 WHERE NAME IN ('memory_max_target',
                'memory_target',
                'sga_max_size',
                'sga_target',
                'shared_pool_size',
                'result_cache_max_size')
 ORDER BY NAME;

众所周知,访问内存比访问硬盘快得多,除非硬盘体系发生革命性的改变。可以说缓存在Oracle里面无处不在,结果集缓存(Result Cache)是Oracle Database 11g新引入的功能,引入它的目的在于可以重用相同的结果集,减少逻辑IO,提高系统性能。结果集缓存又分为:服务端缓存和客户端缓存。


一、服务端缓存


1.服务器端的Result Cache Memorey由两部分组成。
(1)SQL Query Result Cache:存储SQL查询的结果集。
(2)PL/SQL Function Result Cache:用于存储PL/SQL函数的结果集。


 


2.相关的初始化参数:


SQL> show parameter result


NAME TYPE VALUE
------------------------------------ ---------------------- ------------------------------
client_result_cache_lag big integer 3000
client_result_cache_size big integer 0
result_cache_max_result integer 5
result_cache_max_size big integer 10M
result_cache_mode string MANUAL
result_cache_remote_expiration integer 0


 


带client的是客户端的参数,剩下的是服务端的相关参数。


2.1 首先,服务端的结果集缓存的开关,是参数result_cache_max_size。当result_cache_max_size=0的时候,代表不启用结果集缓存。


它的大小,默认值取决于其它内存设置(memory_target的0.25% 或sga_target 的0.5% 或shared_pool_size 的1%)


2.2 其次,result_cache_mode 用于控制Server Result cache的模式


result_cache_mode 有3个值:auto、manual、force


如果设置的值为MANUAL,用户必须用reslut_cache 提示才可以缓存结果集。
如果设置为FORCE,ORACLE会缓冲所有的结果,除非用户用了no_result_cache提示。
如果设置为AUTO,优化器会自动判断是否将查询结果缓存。


2.3 RESULT_CACHE_MAX_RESULT 


该参数是控制单个result所能占据RESULT_CACHE_MAX_SIZE的大小比例,注意是一个百分比。
该参数默认是是5%,取值范围当然是1% ~ 100% 了。


2.4 result_cache_remote_expiration
该参数的作用是根据远程数据库对象设置缓存过期的时间,默认值为0.
也就是说,默认情况下,远程数据库对象不会被进行cache的。


 


3.相关视图


V$RESULT_CACHE_STATISTICS:列出各种缓存设置和内存使用统计数据。
V$RESULT_CACHE_MEMORY:列出所有的内存块和相应的统计信息。
V$RESULT_CACHE_OBJECTS:列出所有的对象(缓存的结果和依赖的对象)和它们的属性。
V$RESULT_CACHE_DEPENDENCY:列出缓存的结果和依赖对象间的依赖详情。


4.Oracle还提供了一个包来管理server result cache:dbms_result_cache


4.1查看server result cache的内存使用报告


SQL> set serveroutput on;


SQL> exec dbms_result_cache.Memory_Report
R e s u l t C a c h e M e m o r y R e p o r t
[Parameters]
Block Size = 1K bytes
Maximum Cache Size = 10M bytes (10K blocks)
Maximum Result Size = 512K bytes (512 blocks)
[Memory]
Total Memory = 110476 bytes [0.018% of the Shared Pool]
... Fixed Memory = 12080 bytes [0.002% of the Shared Pool]
... Dynamic Memory = 98396 bytes [0.016% of the Shared Pool]
....... Overhead = 65628 bytes
....... Cache Memory = 32K bytes (32 blocks)
........... Unused Memory = 0 blocks
........... Used Memory = 32 blocks
............... Dependencies = 1 blocks (1 count)
............... Results = 31 blocks
................... SQL = 1 blocks (1 count)
................... Invalid = 30 blocks (30 count)


PL/SQL procedure successfully completed.


 


4.2 FLUSH: 清空整个结果缓存的内容。
 
4.3 INVALIDATE: 使结果缓存中一个特定对象的缓存结果无效。
 
4.4 INVALIDATE_OBJECT: 根据缓存ID使一特定结果缓存无效。


 


5.实验:


复制代码
SQL> create table test as select * from dba_objects;


Table created.


SQL> select /*+ result_cache */ owner,count(*) from test group by owner;


28 rows selected.




Execution Plan
----------------------------------------------------------
Plan hash value: 1435881708


--------------------------------------------------------------------------------------------------
| Id  | Operation           | Name                       | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |                            | 74795 |  1241K|   291   (2)| 00:00:04 |
|   1 |  RESULT CACHE       | 6fpdv8pg615sh5j0pw1t1jn3wq |       |       |            |          |
|   2 |   HASH GROUP BY     |                            | 74795 |  1241K|   291   (2)| 00:00:04 |
|   3 |    TABLE ACCESS FULL| TEST                       | 74795 |  1241K|   289   (1)| 00:00:04 |
--------------------------------------------------------------------------------------------------


Result Cache Information (identified by operation id):
------------------------------------------------------


   1 - column-count=2; dependencies=(TEST.TEST); parameters=(nls); name="select /*+ result_cache */
 owner,count(*) from test group by owner"




Note
-----
   - dynamic sampling used for this statement (level=2)




Statistics
----------------------------------------------------------
         48  recursive calls
          0  db block gets
       1109  consistent gets
       1031  physical reads
          0  redo size
       1035  bytes sent via SQL*Net to client
        430  bytes received via SQL*Net from client
          3  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
         28  rows processed


SQL> select /*+ result_cache */ owner,count(*) from test group by owner;


28 rows selected.




Execution Plan
----------------------------------------------------------
Plan hash value: 1435881708


--------------------------------------------------------------------------------------------------
| Id  | Operation           | Name                       | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |                            | 74795 |  1241K|   291   (2)| 00:00:04 |
|   1 |  RESULT CACHE       | 6fpdv8pg615sh5j0pw1t1jn3wq |       |       |            |          |
|   2 |   HASH GROUP BY     |                            | 74795 |  1241K|   291   (2)| 00:00:04 |
|   3 |    TABLE ACCESS FULL| TEST                       | 74795 |  1241K|   289   (1)| 00:00:04 |
--------------------------------------------------------------------------------------------------


Result Cache Information (identified by operation id):
------------------------------------------------------


   1 - column-count=2; dependencies=(TEST.TEST); parameters=(nls); name="select /*+ result_cache */
 owner,count(*) from test group by owner"




Note
-----
   - dynamic sampling used for this statement (level=2)




Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
          0  consistent gets
          0  physical reads
          0  redo size
       1035  bytes sent via SQL*Net to client
        430  bytes received via SQL*Net from client
          3  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
         28  rows processed
复制代码
从执行计划中可以看到标黄色的地方,就是对应 v$result_cache_objects的cache_id的。在第二次查询的执行计划中,发现逻辑读已经变成0 了,说明result cache起作用了。


复制代码
SQL> exec dbms_result_cache.memory_report    
R e s u l t   C a c h e   M e m o r y   R e p o r t
[Parameters]
Block Size          = 1K bytes
Maximum Cache Size  = 10M bytes (10K blocks)
Maximum Result Size = 512K bytes (512 blocks)
[Memory]
Total Memory = 110476 bytes [0.018% of the Shared Pool]
... Fixed Memory = 12080 bytes [0.002% of the Shared Pool]
... Dynamic Memory = 98396 bytes [0.016% of the Shared Pool]
....... Overhead = 65628 bytes
....... Cache Memory = 32K bytes (32 blocks)
........... Unused Memory = 0 blocks
........... Used Memory = 32 blocks
............... Dependencies = 3 blocks (3 count)
............... Results = 29 blocks
................... SQL     = 1 blocks (1 count)
................... Invalid = 28 blocks (28 count)


PL/SQL procedure successfully completed.


SQL> select * from v$result_cache_statistics;


        ID NAME                           VALUE
---------- ------------------------------ ------------------------------
         1 Block Size (Bytes)             1024
         2 Block Count Maximum            10240
         3 Block Count Current            32
         4 Result Size Maximum (Blocks)   512
         5 Create Count Success           30166
         6 Create Count Failure           0
         7 Find Count                     3
         8 Invalidation Count             2
         9 Delete Count Invalid           30137
        10 Delete Count Valid             0
        11 Hash Chain Length              1


11 rows selected.


SQL> 
复制代码
再来看看相应的report和统计信息。


复制代码
SQL> select * from v$result_cache_statistics;


        ID NAME                           VALUE
---------- ------------------------------ ------------------------------
         1 Block Size (Bytes)             1024
         2 Block Count Maximum            10240
         3 Block Count Current            32
         4 Result Size Maximum (Blocks)   512
         5 Create Count Success           30166
         6 Create Count Failure           0
         7 Find Count                     3
         8 Invalidation Count             2
         9 Delete Count Invalid           30137
        10 Delete Count Valid             0
        11 Hash Chain Length              1


11 rows selected.


SQL> select /*+ result_cache */ owner,count(*) from test group by owner;


OWNER                                                          COUNT(*)
------------------------------------------------------------ ----------
LZQ                                                                   2
OWBSYS_AUDIT                                                         12
MDSYS                                                              1509
PUBLIC                                                            27696
OUTLN                                                                 9
TEST                                                                  9
CTXSYS                                                              366
OLAPSYS                                                             719
FLOWS_FILES                                                          12
OWBSYS                                                                2
SYSTEM                                                              529


OWNER                                                          COUNT(*)
------------------------------------------------------------ ----------
ORACLE_OCM                                                            8
EXFSYS                                                              310
APEX_030200                                                        2406
SCOTT                                                                 6
ISC                                                                 558
DBSNMP                                                               65
ORDSYS                                                             2532
ORDPLUGINS                                                           10
SYSMAN                                                             3491
SURE                                                                  1
APPQOSSYS                                                             3


OWNER                                                          COUNT(*)
------------------------------------------------------------ ----------
XDB                                                                 842
ORDDATA                                                             248
SS                                                                    1
SYS                                                               30792
WMSYS                                                               316
SI_INFORMTN_SCHEMA                                                    8


28 rows selected.


SQL> select * from v$result_cache_statistics;                           


        ID NAME                           VALUE
---------- ------------------------------ ------------------------------
         1 Block Size (Bytes)             1024
         2 Block Count Maximum            10240
         3 Block Count Current            32
         4 Result Size Maximum (Blocks)   512
         5 Create Count Success           30226
         6 Create Count Failure           0
         7 Find Count                     4
         8 Invalidation Count             2
         9 Delete Count Invalid           30197
        10 Delete Count Valid             0
        11 Hash Chain Length              1


11 rows selected.


SQL> 
复制代码
注意到Find Count = 1,说明上次缓存的结果被后面的查询应用了一次。如果继续执行该查询,可以发现该统计信息会相应增加。


我们可以根据cache_id去 v$result_cache_objects查询相关的信息 


复制代码
SQL> select name,cache_id from v$result_cache_objects t where t.CACHE_ID='6fpdv8pg615sh5j0pw1t1jn3wq'
  2  ;


NAME
------------------------------
CACHE_ID
---------------------------------------------------------------------------------------------------
select /*+ result_cache */ own
er,count(*) from test group by
 owner
6fpdv8pg615sh5j0pw1t1jn3wq
复制代码
 


6.深入认识result cache


query cache result特性所占据的内存是从sga的share pool中分配,如下视图可以看到


SQL> select * from v$sgastat where lower(name) like '%result%';


POOL NAME BYTES
------------------------ ------------------------- ----------
shared pool Result Cache: State Objs 5492
shared pool Result Cache 98396
shared pool Result Cache: Memory Mgr 124
shared pool Result Cache: Bloom Fltr 2048
shared pool Result Cache: Cache Mgr 4416


 


result cache也有类似enqueue/lock的保护机制,RC enqueue就是拿来保护并发修改的。result cache依赖是object level的,
既不是row level的,也不是block level的。任何DML/DDL(甚至包括grant)都会使基于这个object的result cache变为invalidate。
所以result cache只有对那些在平时几乎没有任何DML的只读表比较有用,可以减轻io的压力。


在平时读取阶段不是使用的shared pool latch,而是使用的result cache latch,如下所示




SQL> select * from v$latchname where name like 'Result Cache%';


LATCH# NAME HASH
---------- ------------------------- ----------
391 Result Cache: RC Latch 1054203712
392 Result Cache: SO Latch 986859868
393 Result Cache: MB Latch 995186388


 


检查result cache配置是否生效
1.select dbms_result_cache.status from dual;
2.dbms_result_cache.MEMORY_REPORT
3.查看sql执行计划中是否还有关键字:result cache


注意:
11g的active dataguard的备库是不能使用result cache的,result cache目前可以使用在单节点主库和rac环境。


 


二、客户端查询结果集缓存


假设有这样一个情形:客户端需要通过慢速网络链接调用同一数据。尽管数据库可以立即将结果从缓存发送到客户端,但结果必须通过线路传送到客户端,这就增加了整体执行时间。现在有一些专门的中间件框架(如 Oracle Coherence),用于在 Java、PHP 和 Ruby 中缓存数据,如果有一个在客户端级别缓存数据的通用方法,又将如何呢?


为此,Oracle 数据库 11g 提供了“客户端查询结果缓存”。所有使用 OCI8 驱动程序的数据库客户端堆栈(C、C++、JDBC-OCI 等)都可以使用这一新特性,使客户端能够在本地缓存 SQL 查询的结果,而不是在服务器上。总言之,客户端查询结果缓存可以提供以下好处:


(1)使应用程序开发人员不用构建由所有会话共享的一致的每一流程的 SQL 结果缓存


(2)通过利用更便宜的客户端内存并在本地缓存每个应用程序的工作集,将服务器端查询缓存扩展到客户端内存,


(3)从而消除到服务器的往返过程,确保更好的性能


(4)通过节约服务器资源,改善服务器可伸缩性


(5)提供透明的缓存管理:内存管理、结果集的并发访问等


(6)透明地维护缓存与服务器端更改的一致性


(7)在 RAC 环境中提供一致性


要使用这一特性,所要做的就是设置一个初始化参数:CLIENT_RESULT_CACHE_SIZE = 1G


该参数定义客户端缓存为 1GB,这是所有客户端的总缓存大小。(这是一个静态参数,因此必须重启数据库来设置)可以在每个客户端中设置缓存,在客户端位置的 SQLNET.ORA 文件中指定其他参数: 


让我们看一看它的使用方式。这是简单的 Java 代码,它使用 OCI8 驱动程序连接到数据库,然后执行 SQL 语句:select /*+ result_cache */ * from customers。提示致使语句缓存该结果(其他参数已经设置好)。


复制代码
public class CacheTest {
    private String jdbcURL = "jdbc:oracle:oci8:@orcl";
    private Connection conn = null;
    public CacheTest( ) throws ClassNotFoundException {
            Class.forName("oracle.jdbc.driver.OracleDriver");
        }
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
            CacheTest check = new CacheTest();
            check.dbconnect();
            check.doSomething();
        }
    public void dbconnect() throws SQLException {
            System.out.println("Connecting with URL="+jdbcURL+" as arup/arup");
            try {
                conn = DriverManager.getConnection( jdbcURL, "arup" , "arup");
                System.out.println("Connected to Database");
            } catch (SQLException sqlEx) {
                System.out.println(" Error connecting to database : " + sqlEx.toString());
            }
        }
    public void doSomething() throws SQLException {
            Statement stmt = null;
            ResultSet rset = null;
            try {
                stmt = conn.createStatement();
                System.out.println("Created Statement object");
                rset = stmt.executeQuery("select /*+ result_cache */ * from customers");
                System.out.println("Retrieved ResultSet object");
                if(rset.next())
                System.out.println("Result:"+rset.getString(1));
            } catch (SQLException sqlEx) {
            }finally {
                try {
                    System.out.println("Closing Statment & ResultSet Objects");
                    if (rset != null) rset.close();
                    if (stmt != null) stmt.close();
                    if (conn != null) {
                        System.out.println("Disconnecting...");
                        conn.close();
                        System.out.println("Disconnected from Database");
                    }
                } catch (Exception e) {}
            }
        }
    }
复制代码
 


 PS: OCI8类似于thin,只是它需要在相关的服务器上装上oracle client才能使用。






oracle 11g 函数结果缓存
oracle数据库用一个单独的缓存区为每一个函数同时保存输入和返回值。这个缓存区被连接到这个数据库实例的全部会话共享,而不是为每一个会话复制
每单函数被调用时,数据库就会检查是否已经缓存了相同的输入值。如果是,函数就不用执行了。把缓存中的值简单返回就是了。
每当发现要修改的是缓存所依赖的表,数据库就会自动把缓存失效。

比如我们建立如下的函数
CREATE OR REPLACE FUNCTION f_object (p_id IN number) RETURN VARCHAR2 RESULT_CACHE AS
  v_object varchar2(200);
BEGIN
  select object_name into v_object from t_object where object_id=p_id;
  return v_object;
END ;

--执行函数
SQL> select f_object(10011) from dual;
F_OBJECT(10011)
--------------------------------------------------------------------------------
KU$_IND_SUBNAME_VIEW

Execution Plan
----------------------------------------------------------
Plan hash value: 1388734953
-----------------------------------------------------------------
| Id  | Operation        | Name | Rows  | Cost (%CPU)| Time     |
-----------------------------------------------------------------
|   0 | SELECT STATEMENT |      |     1 |     2   (0)| 00:00:01 |
|   1 |  FAST DUAL       |      |     1 |     2   (0)| 00:00:01 |
-----------------------------------------------------------------

Statistics
----------------------------------------------------------
        20  recursive calls
         0  db block gets
      1053  consistent gets
         0  physical reads
         0  redo size
       447  bytes sent via SQL*Net to client
       419  bytes received via SQL*Net from client
         2  SQL*Net roundtrips to/from client
         0  sorts (memory)
         0  sorts (disk)
         1  rows processed
SQL> select f_object(10011) from dual;
F_OBJECT(10011)
--------------------------------------------------------------------------------
KU$_IND_SUBNAME_VIEW

Execution Plan
----------------------------------------------------------
Plan hash value: 1388734953
-----------------------------------------------------------------
| Id  | Operation        | Name | Rows  | Cost (%CPU)| Time     |
-----------------------------------------------------------------
|   0 | SELECT STATEMENT |      |     1 |     2   (0)| 00:00:01 |
|   1 |  FAST DUAL       |      |     1 |     2   (0)| 00:00:01 |
-----------------------------------------------------------------

Statistics
----------------------------------------------------------
         0  recursive calls
         0  db block gets
         0  consistent gets
         0  physical reads
         0  redo size
       447  bytes sent via SQL*Net to client
       419  bytes received via SQL*Net from client
         2  SQL*Net roundtrips to/from client
         0  sorts (memory)
         0  sorts (disk)
         1  rows processed
SQL> insert into t_object select * from t_object where rownum<2;
1 row created.

Execution Plan
----------------------------------------------------------
Plan hash value: 3381357465
--------------------------------------------------------------------------------
-----
| Id  | Operation                | Name     | Rows  | Bytes | Cost (%CPU)| Time
   |
--------------------------------------------------------------------------------
-----
|   0 | INSERT STATEMENT         |          |     1 |   207 |   297   (1)| 00:00
:04 |
|   1 |  LOAD TABLE CONVENTIONAL | T_OBJECT |       |       |            |
   |
|*  2 |   COUNT STOPKEY          |          |       |       |            |
   |
|   3 |    TABLE ACCESS FULL     | T_OBJECT | 84104 |    16M|   297   (1)| 00:00
:04 |
--------------------------------------------------------------------------------
-----

Predicate Information (identified by operation id):
---------------------------------------------------
  2 - filter(ROWNUM<2)
Note
-----
  - dynamic sampling used for this statement (level=2)

Statistics
----------------------------------------------------------
         9  recursive calls
        29  db block gets
       166  consistent gets
       298  physical reads
      2052  redo size
       678  bytes sent via SQL*Net to client
       630  bytes received via SQL*Net from client
         3  SQL*Net roundtrips to/from client
         2  sorts (memory)
         0  sorts (disk)
         1  rows processed
SQL> commit;
Commit complete.
SQL>  select f_object(10011) from dual;
F_OBJECT(10011)
--------------------------------------------------------------------------------
KU$_IND_SUBNAME_VIEW

Execution Plan
----------------------------------------------------------
Plan hash value: 1388734953
-----------------------------------------------------------------
| Id  | Operation        | Name | Rows  | Cost (%CPU)| Time     |
-----------------------------------------------------------------
|   0 | SELECT STATEMENT |      |     1 |     2   (0)| 00:00:01 |
|   1 |  FAST DUAL       |      |     1 |     2   (0)| 00:00:01 |
-----------------------------------------------------------------

Statistics
----------------------------------------------------------
         2  recursive calls
         0  db block gets
      1047  consistent gets              --插入一行数据后,缓存的结构就失效了
       726  physical reads
         0  redo size
       447  bytes sent via SQL*Net to client
       419  bytes received via SQL*Net from client
         2  SQL*Net roundtrips to/from client
         0  sorts (memory)
         0  sorts (disk)
         1  rows processed
SQL>

这个功能对静态表非常有用














About Me

...............................................................................................................................

● 本文整理自网络

● 本文在itpub(http://blog.itpub.net/26736162)、博客园(http://www.cnblogs.com/lhrbest)和个人微信公众号(xiaomaimiaolhr)上有同步更新

● 本文itpub地址:http://blog.itpub.net/26736162/abstract/1/

● 本文博客园地址:http://www.cnblogs.com/lhrbest

● 本文pdf版及小麦苗云盘地址:http://blog.itpub.net/26736162/viewspace-1624453/

● 数据库笔试面试题库及解答:http://blog.itpub.net/26736162/viewspace-2134706/

● QQ群:230161599     微信群:私聊

● 联系我请加QQ好友(646634621),注明添加缘由

● 于 2017-06-02 09:00 ~ 2017-06-30 22:00 在魔都完成

● 文章内容来源于小麦苗的学习笔记,部分整理自网络,若有侵权或不当之处还请谅解

● 版权所有,欢迎分享本文,转载请保留出处

...............................................................................................................................

拿起手机使用微信客户端扫描下边的左边图片来关注小麦苗的微信公众号:xiaomaimiaolhr,扫描右边的二维码加入小麦苗的QQ群,学习最实用的数据库技术。






来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/26736162/viewspace-2140589/,如需转载,请注明出处,否则将追究法律责任。

一、Result Set Caching 说明


1、概述:Oracle 从11g开始引入了结果集缓存(result cache)的新特性,用于存储经常使用的SQL语句和函数的查询结果,将来语句再执行的时候,oracle直接的访问内存得到结果

2、优点:重用相同的结果集,减少逻辑IO,提高系统性能

3、分类:oracle数据库引擎提供了三种结果集缓存,包括:服务器查询结果集缓存、pl/sql函数结果集缓存和客户端结果集缓存

4、应用场合:访问多

  推荐站点

  • At-lib分类目录At-lib分类目录

    At-lib网站分类目录汇集全国所有高质量网站,是中国权威的中文网站分类目录,给站长提供免费网址目录提交收录和推荐最新最全的优秀网站大全是名站导航之家

    www.at-lib.cn
  • 中国链接目录中国链接目录

    中国链接目录简称链接目录,是收录优秀网站和淘宝网店的网站分类目录,为您提供优质的网址导航服务,也是网店进行收录推广,站长免费推广网站、加快百度收录、增加友情链接和网站外链的平台。

    www.cnlink.org
  • 35目录网35目录网

    35目录免费收录各类优秀网站,全力打造互动式网站目录,提供网站分类目录检索,关键字搜索功能。欢迎您向35目录推荐、提交优秀网站。

    www.35mulu.com
  • 就要爱网站目录就要爱网站目录

    就要爱网站目录,按主题和类别列出网站。所有提交的网站都经过人工审查,确保质量和无垃圾邮件的结果。

    www.912219.com
  • 伍佰目录伍佰目录

    伍佰网站目录免费收录各类优秀网站,全力打造互动式网站目录,提供网站分类目录检索,关键字搜索功能。欢迎您向伍佰目录推荐、提交优秀网站。

    www.wbwb.net