Oracle DBA学习基础篇(一) Oracle体系结构 学习笔记
这里是我通过Oracle DBA实战这本书学习的 Oracle体系结构,大部分是我对书中的内容做的总结,还有遇到不理解的知识点,通过查询资料做的整合。通过这篇博客希望可以让你对Oracle的一些内部原理,整体架构有一些基础的认识。你可以将这篇文章看为一篇学习笔记,用以辅助你对Oracle的学习。因为这里都是一些基础的概念以及原理流程,内容也比较多,可能会很乏味,但是更好的理解有助于你后面对Oracle的学习。
这是我自己通过对分析做出的整体图,先对整体结构有个基础的认识,可以让你理解面的内容更轻松一些。
1. 数据库管理系统引入非常复杂的内存和外存体系结构的主要原因是为了有效的管理稀有的系统资源。
2. 数据库的数据量和输入/输出量都是相当大的,而这些数据一般都存在硬盘 (外存)上,因此硬盘为数据库的一类资源。
3. 因为在计算机中所有的数据的修改操作必须在内存中进行,故内存也是数据库的一类资源,且内存是数据库管理系统中最宝贵的稀有资源。
4. Oracle服务器 = Oracle实例(instance) + Oracle数据库(database)
5. Oracle服务器的三种安装方式:
(1)、基于主机方式:用户可直接在安装了数据库的计算机上登录。
(2)、客户端—服务器(client—server)(两层模型)方式:数据库和客户终端分别安装在不同的计算机上,用户通过网络在客户端*问数据库。
(3)、客户端—应用服务器—服务器(client—application server—server)(三层模型):用户通过个人计算机登录应用服务器,再通过应用服务器访问真正的数据库。
6. Oracle实例是一种访问数据库的机制,由内存结构和一些后台进程组成。其内存结构也成为系统全局区(System Global Area,SGA)。
7. Oracle实例分为单进程实例(一个进程执行全部的Oracle代码,只允许单个用户存取)和多进程实例(由多个进程执行Oracle代码的不同部分,对于每一个连接的用户都有一个进程,允许多个用户同时使用)。
8. 当Oracle客户端应用程序与Oracle实例连接的时候,Oracle会产生一个服务器进程来为这些客户服务,此进程在widnows下表现为线程。此进程的主要作用有:对于用户发出的SQL语句进行语法分析,编译,执行用户的SQL语句,如是数据块不在数据缓冲区时,则从磁盘数据文件将数据读入到SGA的共享数据缓冲区中,将SQL语句的执行结果返回组应用程序。
9. 后台进程有5个必需的:SMON 、 PMON 、 DBWR 、 LGWR 、 CKPT 。
(1)、SMON:SMON(System Monitor),系统监督进程,在实例启动时执行实例恢复,并负责清理不再使用的临时段。具体步骤如下:
执行前滚(roll forward),将已经写到重做日志文件中但还没有写到数据文件中(SCN技术)。
在前滚完成后立即打开数据库,这是在数据文件中可能还有一些没有提交的数据。
回滚没有提交的事务(数据)。服务器进程也可能回滚没有提交的事务,但只回滚到它所用到的加锁的数据行。
SMON进程还要执行如下的磁盘空间的维护工作:
1) 回收或合并数据文件中相连的空闲区。
2) 释放临时段(在执行SQL语句时用作排序的磁盘区),将它们还给临时文件以作为空闲区使用。
(2)、 PMON:PMON(Process Monitor),进程监控里程,在服务器进程出现故障时执行进程恢复,负责清理内存内存和释放该进程所使用的资源。
当系统崩溃时,PMON负责的清理工作:
1) 回滚用户当前的事务。
2) 释放用户所加的所有表一级和行一级的锁。
3) 释放用户所有的其他资源等。
(3)、DBWR:DBWR(Database Writer)进程将缓冲区中的数据写入数据文件,负责数据缓冲区的管理。引进该进程的原因之一可能是为了减少I/O次数。
Oracle11g允许在一个实例上最多启动36个DBWR。DBW0~DBW9和DBWa~DBWz。
当数据缓冲区中的一个数据被修改后,被标记为“脏”,DBWR进程负责将“脏”数据写入磁盘。
DBWR采用LRU算法来替换数据缓冲区中的数据。
DBWR的作用包括:
1) 管理数据缓冲区,以便服务器进程总能找到空闲缓冲区
2) 将所有修改后的缓冲区中的数据写入数据库文件
3) 使用LRU算法将最近使用过的块保留在内存中
4) 通过延迟写来优化磁盘i/o.
在下列情况下DBWR要将脏数据写入磁盘数据文件:
1) 当脏缓冲区的数量超过设定限额。
2) 当所设定的时间间隔已到时。
3) 当有进程需要数据库高速缓冲区却找不到空闲的缓冲区时。
4) 当检查点发生时。
5) 当某个表被删除(drop)或被截断(truncate)时。
6) 当某个表空间被设置为只读状态(read only)时。
7) 当使用类似于ALTER TABLESPACE users BEGIN BACKUP 的命令对某个表空间进行联机备份时。
8) 当某个表空间被设置成脱机状态(offline)或重新设置为正常状态(normal)时等。
(4)、LGWR:LGWR(Log Writer)负责将重做日志缓冲区的记录顺序地写到重做日志文件中。引进该进程的原因之一可能是为了减少I/O量。
先介绍Oracle提交(commit)语句的工作流程:
Oracle服务器使用了快速提交技术(fast commit),可以保证Oracle系统效率以及在系统崩溃时恢复数据。
为此Oracle系统引进了系统变化数(System Change Number,SCN)。无论何时,只要某个事物(transaction)被提交,Oracle服务器都将产生一个SCN并将其赋予该事务的所有数据行。在同一个数据库中,SCN是单调递增且唯一的。为了避免在进行一致性实验时操作系统时钟可能引发的问题,Oracle服务器将SCN作为Oracle的内部时间戳来保证数据文件中数据的同步和数据的读一致性。
当SQL*Plus中发出commit语句之后,Oracle内部操作如下:
服务器进程将把提交的记录连同所产生的SCN一起写入重做日志缓冲区中。
重做日志进程将把重做日志缓冲区中一直到所提交的记录(包括该记录)的所有记录连续的写入重做日志文件中。
Oracle通知用户(进程)提交已经完成。
服务器进程将修改数据库高速缓冲区中相关数据的状态并释放资源和打开锁等。
数据库高速缓冲区中的数据是由DBWR写到数据文件中的。
LGWR的写盘条件是:
1) 事务发生提交,即commit;
2) 日志缓冲区达到总数的1/3时;
3) 超时;
4) DBWR进程需要为检验点清除缓冲区时;
5)每三秒钟。
每一个Oracle实例只有一个LGWR进程。
LGWR将日志信息同步的写入到日志文件组的多个日志成员中,如果组中的一个成员文件被删除或不可使用,LGWR将日志信息继续写到该组的其他文件中,不影响数据库的运行。
(5)、CKPT:CKPT(CheckPoint,检查点)进程出现时,系统对全部数据库文件及控制文件的文件头的同步号进行修改,以保证数据库的同步。
引入目的:提高系统效率和数据库的一致性。
发生时间:当DBWR进程把在SGA中所有已经改变了的数据库高速缓冲区中的数据(包括提交的和未提交的数据)写到数据文件时产生的。
在校验点时间发生时,Oracle要将校验点号码写入相关的数据文件的头文件中,还要将校验点号码、重做日志***、归档日志名称和最低、最高SCN号都写入控制文件中。
以下两个参数是对CKPT有影响的:
LOG_CHECKPOINT_TIMEOUT:决定产生一个检验点的时间间隔,单位为秋,默认为1800;
LOG_CHECKPOINT_INTERVAL:执行一个检验点需要填充的日志文件块的数目,默认为0,表示不起作用。
系统对于数据库的改变信息用系统改变号SCN表示,SCN也叫检验点号,在日志中用CHECKPOINT_CHANGE#表示,它是数据文件,日志文件,控制文件的同步的同步依据,同时存储在这三个中,在系统改变时,检验点号也会同时改变。
引起CKPT产生的主要时机有:
1)日志切换时
2)达到LOG_CHECKPOINT_TIMEOUT指定的间隔时间
3)达到LOG_CHECKPOINT_INTERVAL指定的日志块数
4)数据库关闭
5)DBA强制产生
6)表空间OFFLINE时
10.SGA中包含的内存结构:共享池(shared pool)、数据库高速缓冲区(database buffer cache)、重做日志缓冲区(redo log buffer)和其他的一些结构(如锁和统计数据)等。
11. Oracle数据库是数据的一个集合,也称为物理(外存)结构,由三类操作系统文件组成。
(1)、控制文件(control files):包含了维护和校验数据库一致性所需的信息。
(2)、重做日志文件(redo log files):包含了当系统崩溃后进行恢复所需记录的变换信息。
(3)、数据文件(data files):包含了数据库中真正的数据。
12. Oracle服务器的其他关键文件,不属于数据库。
(1)、初始化参数文件(parameter files):定义了实例的特性,如SGA中一些内存结构的大小,DBWR的个数等。
(2)、密码文件(password files):包含了数据库管理员或操作员用户在启动和关闭实例时所需要的密码。
引入密码文件的原因:当数据库没有开启时,如何验证开启数据库的人是数据库管理员还是操作员。
(3)、归档重做日志文件(archived redo log files):重做日志文件的脱机备份。在系统崩溃后恢复时可能需要这些文件。
13. 用户在向Oracle数据库发出SQL命令之前必须与实例建立连接。
14. 用户进程是不能直接访问数据库的,用户进程是运行在客户端的。
15. 在专用连接的情况下,当用户登录Oracle服务器成功后,Oracle就在服务器所运行的计算机上创建一个服务器进程。该服务器进程只为该用户进程提供服务,用户进程与服务器进程是一对一的关系。
16. 一个用户登录Oracle服务器成功,该用户就与Oracle服务器建立连接,而这中连接的状态就被称为会话。一个会话开始于用户成功登录Oracle服务器,终止于用户退出或非正常终止连接。一个数据库可能同时存在多个会话存在,即用相同的用户名和密码登录多次。
17. 连接是用户进程与Oracle服务器之间的通信路径。与Oracle服务器的三种安装方式相对应。
(1)、基于主机方式:用户进程与Oracle服务器之间的通信路径是通过操作系统内部进程通信
(Inter Process Communication,IPC)机制来建立的。
(2)、客户端—服务器(client—server)(两层模型)方式:用户进程与Oracle服务器之间的通信路径是通过网络协议
(如TCP/IP)来完成的。
(3)、客户端—应用服务器—服务器(client—application server—server)(三层模型):用户个人计算机通过网络与应用 服务器或网络服务器通信,应用服务器或网络服务器通过网络与运行数据库的计算机相连。
18. 当Oracle创建一个服务器进程的同时要为该服务器进程分配一个内存区该内存区称为程序全局区(program Global Area,PGA)。与SGA不同,PGA是一个私有的内存区,不能共享,且只属于一个服务器进程。其随着服务器进程的创建而被分配,随着服务器进程的终止而被回收。
在专用服务器进程的配置情况下,PGA主要包括:
(1)、排序区(sort area):用于处理SQL语句所需的排序。
(2)、Cursor状态区(cursor state):用于指示当前所使用的SQL语句的处理状态。
(3)、会话信息区(session information):包括会话的用户权限和优化统计信息。
(4)、堆栈区(stack space):包括其他的会话变量。
在共享服务器进程或多线程的配置下,(1)、(2)、(3)都将存在于SGA中。如果有large pool就会存在于large pool中,否则存在于共享池中。
Cursor是Current set of rows(当前行集)的缩写,引入这一数据结构的目的就是减少I/O--将磁盘操作变为内存操作。
19. SQL语句的执行主要由用户进程和服务器进程来完成,其他的一些进程可能要辅助完成这一过程。查询语句的处理主要包括编译(parse)、执行(execute)、提取数据(fetch):
(1)、parse:在进行编译时,服务器进程会将SQL语句的正文放入共享池的库高速缓存中并完成以下处理:
(2)、execute:Oracle服务器开始执行SQL语句是因为它已经获得了执行SQL语句所需的全部资源和信息。
(3)、fetch:Oracle服务器进程选择所需的数据行,并在需要时将其排序,最后将结果返回给用户进程。
20. SGA的share pool是由库高速缓存(library cache)和数据字典缓存(data dictionary cache)两部分组成。其中library cache包括共享SQL区和共享PL/SQL区,分别存放SQL和PL/SQL与的以及相关的信息。在Oracle11g中库高速缓存还可以包括SQL或
PL/SQL的执行结果,是其他使用同样SQL或PL/SQL语句的进程就可以直接共享结果,提高效率。
21. SQL语句、PL/SQL块、SQL*Plus命令、T-SQL的区别:
(1)、SQL(Structrued Query Language):SQL是结构化查询语言,以数据库为操作对象的语言。由数据定义语言DDL、数据操纵语言DML、数据控制语言构成DCL以及数据查询语言DQL。当输入SQL语句之后,SQL*Plus将其保存在内部缓冲区中。它不面向过程,即前一条语句与后一条语句无关。当SQL命令输入完毕时,有三种方法可以结束SQL命令:在命令行的末尾输入分号(;)并按回车;在单独一行上用斜杠(/);或用空行表示。
(2)、PL/SQL(Procedural Language/SQL):PL/SQL块同样是以数据库中的数据为操作对象,但由于SQL不具备过程控制功能,所以,为了能够与其他语言一样具备面向过程的处理功能,在SQL中加入了诸如循环、选择等面向过程的处理功能,由此形成了
PL/SQL,是Oracle对SQL标准的扩充。
所有PL/SQL语句的解释均由PL/SQL引擎来完成。使用PL/SQL块可编写存储过程、存储函数、触发器和包等数据库永久对象。PL/SQL是结构化SQL,就是在标准SQL中加入了IF...ELSE... For....等控制过程的SQL。
它吸收了近年来编程语言的许多最高设计特点:如数据封装性、信息隐蔽性、重载和例外处理等。它允许SQL的数据操纵语言和查询语句包含在块结构(block_structured)和代码过程语言中,使PL/SQL成为一个功能强大的事务处理语言。
PL/SQL是块结构语言,是一种过程化语言,属于第三代语言,它与C、C++、Java等语言一样关注于处理细节,可以用来实现比较复杂的业务逻辑。它允许SQL的数据操纵语言和查询语句包含在块结构(block_structured)和代码过程语言中,使PL/SQL成为一个功能强大的事务处理语言。在甲骨文数据库管理方面,PL/SQL是对结构化查询语言(SQL)的过程语言扩展。PL/SQL的目的是联合数据库语言和过程语言。PL/SQL的基本单位叫做一个区段,由三个部分组成:一个声明部分,一个可运行部分,和排除-构建部分。
因为PL/SQL允许混合SQL声明和过程结构,因此可以在将声明发送到甲骨文系统去执行之前使用PL/SQL区段和副程序来组合SQL声明,没有PL/SQL,甲骨文需要就每次处理SQL声明,在网络环境中,这将影响交通流量,而且增加响应时间。
PL/SQL区段只被编译一次并且以可运行的形式储存,以降低响应时间。
(3)、SQL*Plus:SQL*Plus命令主要用来格式化查询结果、设置选择、编辑及存储SQL命令、以设置查询结果的显示格式,并且可以设置环境选项。SQL*Plus是运行解释PL/SQL的程序,你可以在SQL*Plus里运行SQL语句。
(4)、T-SQL(Transact-SQL):T-SQL是 SQL 在 Microsoft SQL Server 上的增强版,它是用来让应用程式与 SQL Server 沟通的主要语言。T-SQL 提供标准 SQL 的 DDL 和 DML 功能,加上延伸的函数、系统预存程序以及程式设计结构(例如 IF 和 WHILE)让程式设计更有弹性。
22.想要实现共享SQL或PL/SQL语句,
(1)、library cache必须要大,只能通过设置共享池的大小来间接设置library cache的大小。
(2)、SQL或PL/SQL语句是能共享的通用代码(generic code)。
23.引入library cache的目的就是共享SQL或PL/SQL代码。Oracle是通过使用LRU(Least Recently Used)的队列(list)或算法(algorithm)来有效管理library cache的。
24. data dictionary cache:当Oracle在执行SQL语句时,服务器进程将把数据文件、表、索引、列、用户和其他数据对象的定义和权限信息放入data dictionary cache。
25.为了共享表、列等数据字典信息,应将data dictionary cache设置的大些。只能通过设置共享池的大小来间接设置data dictionary cache的大小。
9i之后使用SQL>ALTER SYSTEM SET SHARED_POOL_SIZE = 250M命令来改变共享池的大小,但受限于SGA_MAX_SIZE参数。
26. 当执行查询语句时,Oracle提取数据库中的数据的过程:
服务器进程在数据库高速缓冲区(database buffer cache)中搜寻所需要的数据。
找到了 ----- 直接使用不进行磁盘操作。
没找到 ----- 进行磁盘操作,将数据文件中的数据读到数据库高速缓冲区中。
27. 为了共享数据库中的数据,database buffer cache应足够大,9i之后使用DB_CACHE_SIZE参数来动态改变database buffer cache的大小。
28. 9i之后,提供了一个称为内存缓冲区顾问(v$db_cache_advice)的工具来帮助调整database buffer cache的统计信息,其状态有三种:
(1)ON:打开,为该工具分配内存并进行统计信息的收集,有一定的内存和CPU开销。
(2)READY:关闭,为该工具分配内存但不进行统计信息的收集,没有CPU开销。
(3)OFF:关闭,不为该工具分配内存且不进行统计信息的收集,没有内存开销也没有CPU开销。
29. 引入重做日志缓存区的主要目的:数据的恢复。Oracle在使用任何DML或DDL操作改变数据之前都将恢复所需的信息,即在数据库高速缓冲区之前,先写入重做日志缓冲区。
30. SQL语言的分类:
(1)、数据查询语言DQL
数据查询语言DQL基本结构是由SELECT子句,FROM子句,WHERE子句组成的查询块:
SELECT <字段名表>
FROM <表或视图名>
WHERE <查询条件>
(2)、数据操纵语言DML
数据操纵语言DML主要有三种形式:
1) 插入:INSERT
2) 更新:UPDATE
3) 删除:DELETE
(3)、数据定义语言DDL
数据定义语言DDL用来创建数据库中的各种对象-----表、视图、
索引、同义词、聚簇等如:
CREATE TABLE/VIEW/INDEX/SYN/CLUSTER
| | | | |
表 视图 索引 同义词 簇
DDL操作是隐性提交的!不能rollback
(4)、数据控制语言DCL
数据控制语言DCL用来授予或回收访问数据库的某种特权,并控制数据库操纵事务发生的时间及效果,对数据库实行监视等。如:
1) GRANT:授权。
2) ROLLBACK [WORK] TO [SAVEPOINT]:回退到某一点。
回滚---ROLLBACK
回滚命令使数据库状态回到上次最后提交的状态。其格式为:
SQL>ROLLBACK;
3) COMMIT [WORK]:提交。
在数据库的插入、删除和修改操作时,只有当事务在提交到数据库时才算完成。在事务提交前,只有操作数据库的这个人才能有权看到所做的事情,别人只有在最后提交完成后才可以看到。提交数据有三种类型:显式提交、隐式提交及自动提交。下面分别说明这三种类型。
(1) 显式提交
用COMMIT命令直接完成的提交为显式提交。其格式为:
SQL>COMMIT;
(2) 隐式提交
用SQL命令间接完成的提交为隐式提交。这些命令是:
ALTER,AUDIT,COMMENT,CONNECT,CREATE,DISCONNECT,DROP,
EXIT,GRANT,NOAUDIT,QUIT,REVOKE,RENAME。
(3) 自动提交
若把AUTOCOMMIT设置为ON,则在插入、修改、删除语句执行后,
系统将自动进行提交,这就是自动提交。其格式为:
SQL>SET AUTOCOMMIT ON;
31. 全新的数据检索语言DSL
由于SQL语言并不能解决所有数据检索问题,我们提出了一种全新的设计,一种通用的大数据查询语言DSL(domain-specific languages)来实现海量机器数据的检索分析。
我们通过一种边界明确且独立完整的数据处理单元,让他们遵循一致的数据协议,使得它们可以像Unix命令一样按需组合使用,从而达到处理复杂数据的目标。同时也对外提供了公开的API接口,使得数据查询操作可以被嵌入到用户的其它程序中,满足更多、更复杂和更个性化的功能需求。
一个简单的例子:
含义是从elasticsearch存储的main索引中加载数据,并且对数据按level字段等于”error”来进行检索过滤,之后再按”action”字段来进行分组,最终针对每组计算数据对象总数。这样的命令组合本质上同样是对数据的分析处理,但它具有以下特征:
——体现过程性,过程单元简单清晰,组合使用同样简单清晰
——每个命令单元都可以单独工作,达成自己的功能效果
——命令单元可按需组合使用
DSL语言技术应用,属于程序编译技术范畴,除了语言标准规范等方面的规划设计外,自主设计开发了其相关的语言解释器及执行控制器,使得扩展、变更与定制化开发都更加的简单、快速。Parser是根据文法定义动态生成的一个解析执行树,Parser解析DSL后依次得到DSL的语法分析树和抽象语法树,并最终得到可执行的数据结构。修改或者扩展DSL只需要变更定义文件,并且添加新的执行单元即可,不需要变更语法解析逻辑。
DSL语言包含了所有的搜索命令以及可以结合命令使用的函数和表达式。整个DSL的语法风格脱胎于Unix命令管道(Unix pipeline)的运用方法。使用DSL可以对数据进行检索,并且针对结果集执行进一步的修改、转换、插入等操作。比如说针对初步的搜索结果数据集,进行更多信息的抽取、添加新的字段、执行统计、重新排序等等。DSL语言默认提供了7大类搜索方法。
统计命令:统计命令由统计函数与其相关参数组成,不同的参数区别于不同的类型和数量,可以快速的进行聚合、分类统计。
分组标准:分组标准定义作用于数据检索结果,依据相应的分组标准将总数据集划分成若干子集。
范围查询:可快速发现满足一定条件参数的事件,例如检索交易响应时间大于、等于、小于800ms的事件。
正则表达式:可以定义正则表达式匹配检索的事件,得到想要的检索结果。
布尔值:利用AND、OR、NOT和( )的组合,可以*构造各种复杂的组合查询语句。
字段过滤:字段过滤可以显示所有的结构化/半结构化字段的统计聚合,点击其中的字段值进行过滤操作,可快速缩小事件的查询范围。
高级搜索:运用transction、if、event等命令进行关联关系分析、逻辑判断等高级搜索。
32. Oracle在执行DML语句时只有parse和execute两个阶段,以UPDATE语句为例:
如果数据和回滚数据不在数据库高速缓冲区中,则Oracle服务器进程将把它们从数据文件中读到数据库高速缓冲区中。
Oracle服务器进程在要修改的数据行上加锁(行一级的锁,而且是在内存的数据行上加锁)。
Oracle服务器进程将数据的变化信息和回滚多需要的信息都记录在重做日志缓冲区中。
Oracle服务器进程将回滚所需的原始值和对数据所做的修改都写入数据库高速缓冲区中。之后在数据库高速缓冲区中,将所有的这些数据库都标记为脏数缓冲区,因为此时内外存的数据是不一致的。
33. SGA还有可能包含大池(large pool)和java池(java pool)两个可选的内存结构。
34. 引入large pool的 主要目的是提高效率。其没有LRU队列,在多线程(MTS)或共享服务器(shared server)连接是,Oracle服务器进程的PGA的大部分区域将放入large pool(stack space 除外)。另外,在大规模I/O及备份和恢复操作时可能使用该区。可以通过设置参数LARGE_POOL_SIZE的值来动态配置large pool的大小。
35. 引入java pool的目的就死能够编译java语言的命令。Java语言在Oracle数据库中的存储与PL/SQL语言几乎完全相同。可容易通过设置参数JAVA_POOL_SIZE的值来配置java pool的大小。其数字的单位是字节B。
36. Oracle是利用所谓的区组(granule)来管理SGA的内存的。区组就是一片连续的虚拟内存区,是Oracle分配和回收内存的基本单位。SGA至少包括的三个区组:
(1)SGA固定区(其中包括了重做日志缓冲区)
(2)数据库高速缓冲区
(3)共享池
37. Oracle10g/11g中,SGA的尺寸小于或等于1GB时,区组大小为4MB,大于1GB时,区组的大小就为16MB。Oracle数据库管理员可通过ALTER SYSTEM SET 命令来分配和回收区组。但总的内存大小不能超过SGA_MAX-SIZE所设置的值。
38. 动态分组和回收内存的最大优点是在调整内存缓冲区大小时不需要重新启动数据库。
总结:理解与Oracle数据库管理系统相关的基本概念和原理,对于以后的学习还是帮助很大的,很多专业名词后面我都加了英文,因为OCP考试是全英文的,还是趁早熟悉比较好,学好英语还是有百益而无一害的。最后说一点,这里的内容来处五花八门,通过整合删选,选出的这些内容,对各个出处表示感谢!