Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >NULL 值与索引(二)

NULL 值与索引(二)

作者头像
Leshami
发布于 2018-08-14 03:05:46
发布于 2018-08-14 03:05:46
1.5K00
代码可运行
举报
文章被收录于专栏:乐沙弥的世界乐沙弥的世界
运行总次数:0
代码可运行

    在NULL值与索引(一)中讲述了null值与索引的一些基本情况。其主要的内容为,基于允许存在null值的索引列,其索引值不会被存储;其次 是由于这个特性导致了我们在使用is null时索引失效的情形;最后则是描述的通过为null值列添加not null约束来使得is null走索引。尽管我 们可以通过添加not null来解决is null走索引,当现实中的情况是仍然很多列根本是无法确定的,而必须保持其null特性。对于此种情形该如 何解决呢? 一、通过基于函数的索引来使得is null使用索引

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
-->演示环境
scott@ORCL> select * from v$version where rownum<2;

BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Prod

-->创建测试表t2
scott@ORCL> create table t2(obj_id,obj_name) as select object_id,object_name from dba_objects;

Table created.

-->演示表t2上不存在not null约束
scott@ORCL> desc t2
 Name                          Null?    Type
 ----------------------------- -------- --------------------
 OBJ_ID                                 NUMBER
 OBJ_NAME                               VARCHAR2(128)

-->为表t2创建一个普通的B树索引
scott@ORCL> create index i_t2_obj_id on t2(obj_id);

Index created.

-->将表t2列obj_id<=100的obj_id置空
-->注:在Oracle 10g中空字符串等同于null值
scott@ORCL> update t2 set obj_id='' where obj_id<=100;

99 rows updated.

-->下面的查询亦表明在此时空字符串等同于null值
scott@ORCL> set null unknown
scott@ORCL> select * from t2 where obj_id is null and rownum<3;

    OBJ_ID OBJ_NAME
---------- ------------------------------
unknown    ICOL$
unknown    I_USER1

-->收集统计信息
scott@ORCL> exec dbms_stats.gather_table_stats('SCOTT','T2',cascade=>true);

PL/SQL procedure successfully completed.

-->基于null值上使用not null会使用索引扫描,等同于前面 null值与索引() 中的描述
scott@ORCL> select count(*) from t2 where obj_id is not null;

Execution Plan
----------------------------------------------------------
Plan hash value: 3840858596

-------------------------------------------------------------------------------------
| Id  | Operation             | Name        | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |             |     1 |     5 |     7   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE       |             |     1 |     5 |            |          |
|*  2 |   INDEX FAST FULL SCAN| I_T2_OBJ_ID | 11719 | 58595 |     7   (0)| 00:00:01 |
-------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - filter("OBJ_ID" IS NOT NULL)

-->列obj_id is null走全表扫描
scott@ORCL> select count(*) from t2 where obj_id is null;

Execution Plan
----------------------------------------------------------
Plan hash value: 3321871023

---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |     1 |     5 |    13   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE    |      |     1 |     5 |            |          |
|*  2 |   TABLE ACCESS FULL| T2   |     1 |     5 |    13   (0)| 00:00:01 |
---------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - filter("OBJ_ID" IS NULL)

-->创建基于函数的索引来使得is null走索引
-->下面使用了nvl函数来创建函数索引,即当obj_id为null值时,存储-1   
scott@ORCL> create index i_fn_t2_obj_id on t2(nvl(obj_id,-1));

Index created.

-->收集索引信息
scott@ORCL> exec dbms_stats.gather_index_stats('SCOTT','I_FN_T2_OBJ_ID');

PL/SQL procedure successfully completed.

-->可以看到下面的执行计划中刚刚创建的函数索引已经生效I_FN_T2_OBJ_ID
scott@ORCL> select count(*) from t2 where nvl(obj_id,-1) = -1;

Execution Plan
----------------------------------------------------------
Plan hash value: 3983750858

------------------------------------------------------------------------------------
| Id  | Operation         | Name           | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |                |     1 |     5 |     1   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE   |                |     1 |     5 |            |          |
|*  2 |   INDEX RANGE SCAN| I_FN_T2_OBJ_ID |   100 |   500 |     1   (0)| 00:00:01 |
------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access(NVL("OBJ_ID",(-1))=(-1))

二、使用伪列创建基于函数的索引来使得is null使用索引

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
-->下面通过添加一个值为-1(可取任意值)的伪列来创建索引
scott@ORCL> create index i_new_t2_obj_id on t2(obj_id,-1);

Index created.

-->收集索引信息
scott@ORCL> exec dbms_stats.gather_index_stats('SCOTT','I_NEW_T2_OBJ_ID');

PL/SQL procedure successfully completed.   

-->从下面的查询可以看出obj_id is null使用了刚刚创建的索引
scott@ORCL> select count(*) from t2 where obj_id is null;

Execution Plan
----------------------------------------------------------
Plan hash value: 801885198

-------------------------------------------------------------------------------------
| Id  | Operation         | Name            | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |                 |     1 |     5 |     2   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE   |                 |     1 |     5 |            |          |
|*  2 |   INDEX RANGE SCAN| I_NEW_T2_OBJ_ID |    99 |   495 |     2   (0)| 00:00:01 |
-------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access("OBJ_ID" IS NULL)

-->查看刚刚创建的所有索引的相关统计信息   
scott@ORCL> select index_name,index_type,blevel,leaf_blocks,num_rows,status,distinct_keys
  2  from user_indexes where table_name='T2';

INDEX_NAME      INDEX_TYPE                         BLEVEL LEAF_BLOCKS   NUM_ROWS STATUS   DISTINCT_KEYS
--------------- ------------------------------ ---------- ----------- ---------- -------- -------------
I_FN_T2_OBJ_ID  FUNCTION-BASED NORMAL                   1          26      11719 VALID            11621
I_NEW_T2_OBJ_ID FUNCTION-BASED NORMAL                   1          32      11719 VALID            11621
I_T2_OBJ_ID     NORMAL                                  1          25      11620 VALID            11620

-->从上面的结果可知:
-->普通的B索引(I_T2_OBJ_ID)使用的索引块最小,因为null值没有被存储,NUM_ROWSDISTINCT_KEYS即是佐证
-->使用NVL函数创建的索引I_FN_T2_OBJ_ID中如实的反应了null值,即11620 + null= 11621
-->使用伪列创建的索引依然属于函数索引,其耗用的叶节点块数最多,因为多出了一个值(-1)来存储
-->尽管使用NVL创建的函数占用的磁盘空间小于使用伪列创建的索引,当在书写谓词时需要带上NVL函数,而伪列索引中谓词直接使用is null

三、NULL值与索引衍生特性

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
-->由前面的种种事例再次说明NULL值不会被存储到索引中,因此基于这个特性可以使用decode函数来压缩索引列。
-->在实际应用的多数情形中,如表上有打印状态列is_printed通常为两种情形,已打印或未打印,假定1表示已打印,而0表示未打印。
-->通常情况下90%以上的单据都处于已打印状态,而仅有10%左右的处于未打印。而经常要使用的情形是查询未打印的单据并重新打印。
-->基于上述情况,可以使用位图索引来解决,但此处我们讨论的是B树索引,故不考虑该情形(或者说你使用了非企业版Oracle,不支持位图索引)
-->此处对于这类情形我们可以使用decode函数来解决这个问题

-->更新表上的列,使之obj_id为1的行占绝大多数
scott@ORCL> update t2 set obj_id=1 where obj_id is not null;

11620 rows updated.

-->更新表,使之obj_id为0的行占少部分
scott@ORCL> update t2 set obj_id = 0 where obj_id is null;

99 rows updated.

scott@ORCL> commit;

-->收集统计信息
scott@ORCL> exec dbms_stats.gather_table_stats('SCOTT','T2',cascade=>true);

PL/SQL procedure successfully completed.

-->表t2上obj_id列的最终分布
scott@ORCL> select obj_id,count(*) from t2 group by obj_id;

    OBJ_ID   COUNT(*)
---------- ----------
         1      11620
         0         99   

-->使用decode函数创建索引
-->注意此处decode的使用,当obj_id非0值时,其值被赋予为null值,由于该null值不会存储到索引,因此大部分obj_id列值为1的不会被索引
scott@ORCL> create index i_fn2_t2_obj_id on t2(decode(obj_id,0,0,null));

Index created.

-->收集索引上的统计信息
scott@ORCL> exec dbms_stats.gather_index_stats('SCOTT','I_FN2_T2_OBJ_ID');

PL/SQL procedure successfully completed.

-->查看新索引的执行计划
scott@ORCL> set autot trace exp;
scott@ORCL> select count(*) from t2 where decode(obj_id,0,0,null) = 0;

Execution Plan
----------------------------------------------------------
Plan hash value: 1461308992

-------------------------------------------------------------------------------------
| Id  | Operation         | Name            | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |                 |     1 |     3 |     1   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE   |                 |     1 |     3 |            |          |
|*  2 |   INDEX RANGE SCAN| I_FN2_T2_OBJ_ID |    98 |   294 |     1   (0)| 00:00:01 |
-------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access(DECODE("OBJ_ID",0,0,NULL)=0)

-->当直接使用obj_id = 0来查询时使用的是普通的B树索引
scott@ORCL> select count(*) from t2 where obj_id = 0;

Execution Plan
----------------------------------------------------------
Plan hash value: 1804118247

---------------------------------------------------------------------------------
| Id  | Operation         | Name        | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |             |     1 |     3 |     1   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE   |             |     1 |     3 |            |          |
|*  2 |   INDEX RANGE SCAN| I_T2_OBJ_ID |    99 |   297 |     1   (0)| 00:00:01 |
---------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access("OBJ_ID"=0)   

-->当使用obj_id = 1来查询时走全表扫描,因为obj_id = 1占据表90%以上,由CBO特性决定了走全表扫描   
scott@ORCL> select * from t2 where obj_id = 1;

Execution Plan
----------------------------------------------------------
Plan hash value: 1513984157

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      | 11620 |   249K|    14   (8)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| T2   | 11620 |   249K|    14   (8)| 00:00:01 |
--------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("OBJ_ID"=1)
   
-->表t2上所有索引的统计信息
scott@ORCL> select index_name,index_type,blevel,leaf_blocks,num_rows,status,distinct_keys
  2  from user_indexes where table_name='T2';
  
INDEX_NAME      INDEX_TYPE                         BLEVEL LEAF_BLOCKS   NUM_ROWS STATUS   DISTINCT_KEYS
--------------- ------------------------------ ---------- ----------- ---------- -------- -------------
I_FN_T2_OBJ_ID  FUNCTION-BASED NORMAL                   1          40      11719 VALID                2
I_NEW_T2_OBJ_ID FUNCTION-BASED NORMAL                   1          52      11719 VALID                2
I_FN2_T2_OBJ_ID FUNCTION-BASED NORMAL                   0           1         99 VALID                1
I_T2_OBJ_ID     NORMAL                                  1          40      11719 VALID                2

-->从上面的结果可知,索引I_FN2_T2_OBJ_ID仅仅存储了99跳记录,且DISTINCT_KEYS值为1个,因为所有非0值的全部被置NULL-->以上方法实现了索引压缩,避免了较大索引维护所需的开销,同时也提高了查询性能。
-->Author : Robinson Cheng
-->Blog :   http://blog.csdn.net/robinson_0612

四、总结     1、对于用于连接或经常被谓词使用到的列应尽可能避免NULL值属性,因为它容易导致索引失效。     2、为需要使用NULL值的列添加缺省值(alter table tb modify(col default 'Y'))。     3、如果NULL值不可避免也不能使用缺省值,应考虑为该常用列使用nvl函数创建索引,或使用伪列来创建索引以提高查询性能。     4、对于复合索引应保证索引中至少有一列不为NULL值,还是因为全部列为NULL时不被索引存储,以保证使用is null是可以使用索引。     5、对于复合索引应保证索引列应使用数据类型长度最小的列来添加not null约束应节省磁盘空间。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2012年04月08日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
NULL 值与索引(一)
    NULL值是关系数据库系统布尔型(true,false,unknown)中比较特殊类型的一种值,通常称为UNKNOWN或空值,即是未知的,不确定的。由于 NULL存在着无数的可能,因此NULL值也不等于NULL值,所以与NULL值相关的操作同样都为NULL值。正是基于这样一个特性,对于NULL值列上的B 树索引导致了is null/is not null不走索引的情形,下面描述了NULL值与索引以及索引NULL列上的执行计划,如何使得NULL值走索引的情形。 注:本文仅仅讨论的是B树索引上的NULL值,位图索引不在此范围之内。 一、null值与索引的关系
Leshami
2018/08/14
1.6K0
【DB笔试面试567】在Oracle中, IS NULL如何用到索引?
IS NULL用于判断某一列中的值是否为空。当IS NULL作为WHERE条件的时候,该列是不会用到索引的,但是可以加伪列创建伪联合索引来使得IS NULL使用索引,看如下的例子:
AiDBA宝典
2019/09/29
6990
使用 EXPLAIN PLAN 获取SQL语句执行计划
     SQL查询语句的性能从一定程度上影响整个数据库的性能。很多情况下,数据库性能的低下差不多都是不良SQL语句所引起。而SQL语句的执行 计划则决定了SQL语句将会采用何种方式从数据库提取数据并返回给客户端,本文描述的将是如何通过EXPLAIN PLAN 获取SQL语句执行计划来获 取SQL语句的执行计划。
Leshami
2018/08/14
1.2K0
Oracle优化05-执行计划
如果要分析某条SQL的性能问题,通常来讲,我们首先要看SQL的执行计划,看看SQL的每一步执行计划是否存在问题。
小小工匠
2021/08/16
8030
【DB笔试面试475】分区表性能注意事项有哪些?
② 在设计分区表时,避免数据都进入默认分区,从而导致出现默认分区超大或各个分区大小严重不均衡的情况,失去分区表的意义。
AiDBA宝典
2019/09/30
5750
通过索引提升SQL性能案例一则
最近有个应用,前端调用后台的一个逻辑很慢,请开发提供了对应逻辑使用的SQL,进行脱敏,示例如下,
bisal
2021/03/23
2910
Oracle SQL性能分析之10053事件
优化器生成正确执行计划的前提条件是要有正确的统计信息,不准确的统计信息往往会导致错误的执行计划。当通过SQL和基数推断出的执行计划和实际执行计划不同时,就可以借助10053事件。10053事件是用来诊断优化器如何估算成本和选择执行计划的,用它产生的trace文件提供了Oracle如何选择执行计划,为什么会得到这样的执行计划信息。和10046事件类似,它主要用于特殊情况下的分析和诊断。
星哥玩云
2022/08/17
6760
Oracle优化07-分析及动态采样-直方图
获取准确的段对象(表、表分区、索引等)的分析数据,是CBO存在的基石。所以数据段的分析对于CBO来讲非常的重要。
小小工匠
2021/08/16
3120
【DB笔试面试593】在Oracle中,表的访问方式有哪几种?
访问表的方式也叫优化器访问路径,主要有3种访问路径:全表扫描(FULL TABLE SCAN,FTS)、索引扫描(INDEX SCAN)和ROWID访问。
AiDBA宝典
2019/09/29
1.2K0
【DB笔试面试593】在Oracle中,表的访问方式有哪几种?
高效SQL语句必杀技
        No SQL,No cost. SQL语句是造成数据库开销最大的部分。而不良SQL写法直接导致数据库系统性能下降的情形比比皆是。那么如何才能称得 上高效的SQL语句呢?一是查询优化器为当前的SQL语句生成最佳的执行计划,保证数据读写使用最佳路径;二是设置合理的物理存储结构,如表 的类型,字段的顺序,字段的数据类型等。本文主要描述如何编写高效的SQL语句并给出示例。下面的描述主要分为三个部分,一是编写高效SQL 语句,二是使用索引提高查询性能的部分,三是总结部分。
Leshami
2018/08/14
1.4K0
Oracle 和 Mysql 的索引在Null字段上处理的异同
本文作者系Scott(中文名陈晓辉),ORACLE数据库专家,就职于甲骨文中国。个人主页:segmentfault.com/u/db_perf ,经其本人授权发布。
SQLplusDB
2022/08/22
1.1K0
Oracle 聚簇因子(Clustering factor)
    聚簇因子是 Oracle 统计信息中在CBO优化器模式下用于计算cost的参数之一,决定了当前的SQL语句是否走索引,还是全表扫描以及是否作为嵌套连接外部表等。如此这般,那到底什么是聚簇因子,那些情况下会影响到聚簇因子,以及如何提高聚簇因子?本文将对此展开描述。
Leshami
2018/08/14
1.7K0
Oracle 聚簇因子(Clustering factor)
【DB笔试面试553】在Oracle中,什么是不可见索引?
索引维护是DBA的一项重要工作。当一个系统运行很长一段时间,经过需求变更、结构设计变化后,系统中就可能会存在一些不再被使用的索引,或者使用效率很低的索引。这些索引的存在,不仅占用系统空间,而且会降低事务效率,增加系统的负载。因此,需要找出那些无用或低效的索引,并删除它们(找出无用索引可以通过索引监控的方法)。但是,直接删除索引还是存在一定风险的。例如,某些索引可能只是在一些周期的作业中被使用到,而如果监控周期没有覆盖到这些作业的触发点,那么就会认为索引是无用的,从而将其删除。当作业启动后,可能就会对系统性能造成冲击。这时,可能就会手忙脚乱地去找回索引定义语句、重建索引。在Oracle 11g里,Oracle提供了一个新的特性来降低直接删除索引或者禁用索引的风险,那就是不可见索引(Invisible Indexes)。
AiDBA宝典
2019/09/29
6770
Oracle 全表扫描及其执行计划(full table scan)
    全表扫描是Oracle访问数据库表是较为常见的访问方式之一。很多朋友一看到SQL语句执行计划中的全表扫描,就要考虑对其进行修理一番。全表扫描的存在,的确存在可能优化的余地。但事实上很多时候全表扫描也并非是最低效的,完全要看不同的情形与场合,任一方式都是有利有弊的,也就是具体情况要具体分析。本文描述了什么是全表扫描以及何时发生全表扫描,何时全表扫描才低效。   本文涉及到的相关链接: 高水位线和全表扫描 启用 AUTOTRACE 功能 Oracle 测试常用表BIG_TABLE Oracle
Leshami
2018/08/13
2.8K0
Oracle 索引扫描的几种类型
1.对于unique index来说,如果where 条件后面出现了<,> ,between ...and...的时候,那么就可能执行index range scan,如果where条件后面是=,那么就会执行index unique scan。
星哥玩云
2022/08/17
6200
【DB笔试面试258】在Oracle中,执行计划里的access和filter有什么区别(上)?
一般而言,access表示这个谓词条件的值将会影响数据的访问路径(表还是索引);filter表示谓词条件的值不会影响数据的访问路劲,只起到过滤的作用。NOT IN或MIN函数等容易产生filter操作。
AiDBA宝典
2019/09/30
1.4K0
【DB笔试面试258】在Oracle中,执行计划里的access和filter有什么区别(上)?
Oracle ROWID 方式访问数据库
    和ROWNUM一样,ROWID是一个伪列,即是一个非用户定义的列,而又实际存储于数据库之中。每一个表都有一个ROWID列,一个ROWID值用于 唯一确定数据库表中的的一条记录。因此通过ROWID 方式来访问数据也是 Oracle 数据库访问数据的实现方式之一。一般情况下,ROWID方式的 访问一定以索引访问或用户指定ROWID作为先决条件,因为所有的索引访问方式最终都会转换为通过ROWID来访问数据记录。(注:index full scan 与index fast full scan除外)由于Oracle ROWID能够直接定位一条记录,因此使用ROWID方式来访问数据,极大提高数据的访问效率。
Leshami
2018/08/14
2.1K0
聚簇因子和执行计划的联系(r3笔记第90天)
在平时的工作中,可能会碰到一种很奇怪的问题,本来在生产环境中有些sql语句执行没有问题,一个很普通的查询预期走了索引扫面,但是拷贝数据到其它环境之后,就发现却走了全表扫描。 或者情况相反,本来出现问题的查询走了全表扫描,我们尝试在测试环境中浮现,但是测试环境中在相同的数据量的情况下,查询却又走了索引扫描,问题无法复现了。 出现这种情况的原因比较复杂,涉及很多的原因,其中一个很重要的原因就是聚簇因子的导致的。 聚簇因子是一个与索引相关的统计信息,它通过查看表中的数据块来进行计算得到。 对于这个问题,可能直接说
jeanron100
2018/03/15
6020
INDEX FULL SCAN vs INDEX FAST FULL SCAN
     INDEX FULL SCAN 与 INDEX FAST FULL SCAN两个长相差不多,乃是一母同胞,因此既有其共性,也有其个性。两者来说其共性是不用扫描表而是通过索引就可以直接返回所需要的所有数据。这对提高查询性能而言,无疑是一个难得的数据访问方式之一,因为索引中存储的数据通常是远小于原始表的数据。下面具体来看看两者之间的异同。
Leshami
2018/08/14
2.4K0
关于索引和空值的讨论(r3笔记第80天)
在日常的工作中,空值总是有特殊的身份,对于它的处理有时候也是比较纠结。 有时候创建索引的时候会因为空值出现一些奇怪的结果。 有时候一个简单的查询因为空值却走不了索引。 有时候却因为空值而能走索引。 我们来简单的模拟一下这些问题。 首先创建一个空表,注意对于id列我们是加了not null的约束的。 SQL> create table index_test(id number not null,name varchar2(30) ) ; Table created. 我们创建一个唯一性索引,包含了id
jeanron100
2018/03/15
7450
推荐阅读
相关推荐
NULL 值与索引(一)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档