• Oracle 服务器结构


    【学习目标】


            作为一个数据库管理员(DBA),经常会遇到各种没有见过的问题。除了宝贵的经验外, 通过理论基础去对问题进行判断、解决是至关重要的。因此,Oracle 服务器的结构和组成 是学习Oracle 数据库必不可少的基础知识,所以本章内容对于学习以后章节的内容都是至 关重要的。
             本章主要介绍Oracle 服务器的结构和组成,对Oracle 服务器从实例结构和数据库结构 两个方面予以分析。
    【本章要点】


    • ● Oracle 服务器的总体结构
      ● Oracle 实例的内存结构和进程结构
      ● Oracle 数据库的结构和组成


    【关键术语】


    • Architecture 结构
    • Component 组成部件
      Process 进程
    • Parameter 参数
      Instance 实例
    • Memory 内存
      Database buffer cache 数据库高速缓存
    • Shared pool 共享池
      Library cache 库缓存
    • Data dictionary cache 数据字典缓存
      Redo log buffer 重做日志缓存
    • Large pool 大池
      Java pool java 池
    • Background process 后台进程
      Connection 连接
    • Session 会话
      Online redo log 联机重做日志
    • Dynamic 动态的
      SGA(System Global Area) 系统全局区
    • PGA(Program Global Area)程序全局区
      Server process 服务器进程
    • User process 用户进程
      Parsing 分析

    一、Oracle 服务器的构成


             Oracle 服务器是一个具有高性能和高可靠性面向对象关系型数据库管理系统,也是一 个高效的SQL 语句执行环境。
         Oracle 服务器具备以下的特点:

    • ● 能够可靠的进行多用户环境下大量数据的处理,允许多用户同时访问相同的数据。
      ● 保证数据访问的高性能。
      ● 有效防止对数据的非法访问。
      ● 对于故障恢复提供高效的解决方案。

    (一)、Oracle 服务器的总体结构


           Oracle 服务器同运行在操作系统下的很多程序一样,通过在后台运行一组可执行程序、 在内存中开辟程序运行的存储区域并在磁盘上存储数据来进行运作。
           如图1-1 所示,在结构上,Oracle 服务器(Oracle Server)由实例(Instance)和数 据库(Database)两大部分构成。实例是一组内存结构和Oracle 后台进程的集合;数据库 在物理上由多个操作系统文件组成, 其中主要包括数据文件、控制文件和重做日志文件。
    Oracle 服务器=实例+数据库。

    image

    (二)、Oracle 实例


         如图1-1 所示,Oracle 实例由内存结构(SGA)和Oracle 后台进程组成的。 当用户启动(start)实例时,Oracle 将自动地为SGA 分配内存并启动后台进程,实 例运行后,用户可以访问实例,由实例来访问数据库。实例相当于用户和数据库的中间层。 当用户关闭(shut down)实例时,由操作系统负责回收内存。每个实例都有自己的 SGA,并且,每个实例同时只能访问一个数据库。 Oracle 数据库实例(Instance) = 内存结构(SGA) + 后台进程(Background Process)。
    内存结构描述的是Oracle 数据库对内存的使用构成。Oracle 内存结构被总称为SGA (System Global Area),主要包括数据库高速缓冲区(Database Buffer Cache)、重做日 志缓冲区(Redo Log Buffer)和共享池(Shared Pool)三个部分 Oracle 后台进程主要包括DBWn、CKPT、LGWR、SMON、PMON、ARCn 和RECO。

    image

    (三)、Oracle 数据库

        如图1-2 所示,Oracle 数据库包含了用于存放数据的数据文件(DataFiles),用于保护 数据不丢失的联机重做日志文件(Online Redolog Files)和用于连接Oracle 实例和数据库 所需要的控制文件(Control Files)。
         Oracle 数据库= 数据文件+ 控制文件+ 联机重做日志文件。
    除了以上三种文件之外,Oracle 还包含了参数文件(Parameter File)、口令文件 (Password File)、归档日志文件(Archived log Files)、以及跟踪文件(Trace File)和警 告文件(Alert Log File)等非数据库文件。

    image

    控制文件:记录了数据库的各项信息,是连接Oracle 实例和Oracle 数据库的桥梁。 每个Oracle 数据库包含1-8 个控制文件。
    联机重做日志文件:记录了用户对数据的各项操作,用于保护数据不丢失。 以日志组的形式存在。每个Oracle 数据库至少包含两个日志组。
    数据文件:用于存放数据。
    参数文件:记录了Oracle 实例的各项信息。分为动态和静态初始化参数文件。
    口令文件:存放特定用户的口令。
    归档日志文件:相当于联机重做日志文件的备份,用于保护数据不丢失。
    追踪文件:存放后台进程和服务器进程的跟踪信息。
    警告文件:警告文件由连续的消息和错误组成。 可以查看到Oracle 内部错误、块损坏错误以及非默认的初始化参数值等。

    二、Oracle 内存结构

    Oracle 的内存由系统全局区(System Global Area,简称SGA)和程序全局区 (Program Global Area,简称PGA)组成。

    (一)、系统全局区SGA


              系统全局区(system global area,SGA)一组包含了Oracle 数据库数据及实例控 制信息的共享的内存结构。当多个用户并发地连接到同一个实例后,这些用户将共享此实例 SGA 中的数据。因此SGA 也被称为共享全局区(shared global area)。
             SGA 越大系统的性能越好,但SGA 过大也会起到反作用。一般情况下,SGA 不应超 过系统实际内存的1/2。但从Oracle 9i 开始支持动态SGA 特性,即无需关闭实例就可以改 变数据高速缓冲区、共享池和大池的大小。数据高速缓冲区、共享池的大小也可以根据工作 负载自动调整,但SGA 的总大小不能超过初始化参数SGA_MAX_SIZE 的设置。。

    1.SGA 的构成


       如图2-1 所示,SGA 包括三个子缓冲区:保留缓冲区、循环缓冲区和非标准大小块缓 冲区。SGA 包括以下结构:


    • ● 数据高速缓冲区(Data Buffer Cache)
      ● 重做日志缓冲区(Redo Log Buffer)
      ● 共享池(Shared Pool)
      ● Java 池(Java Pool)
      ● 大池(Large Pool)
      ● 流池(Stream Pool)

    image

    1.1 数据库高速缓冲区(Database Buffer Cache)


           在Oracle 中无论是读取还是修改数据,都是由服务器进程在数据库高速缓冲区中完成 的。数据库高速缓冲区的作用就是用来缓存最近从数据库中读出的数据块,并可供其他客户 进程共享。用户进程查看数据时,首先检查需要的信息是否在缓冲区内,如果在缓冲区中, 就可以直接访问,否则访问物理文件并读取数据块到数据库缓冲区。我们知道内存的读写速 度要比物理设备的读写速度快很多,这样就可以大大提高Oracle 数据库的性能。

    1) 数据库高速缓冲区的子缓冲区


    根据缓冲数据块的状态,Oracle 将数据高速缓冲区可划分成:
    ● 脏缓冲区(Dirty Buffer):
           当数据库发生DML(Insert、Update、Delete)操作时,会对缓冲区内容进行修改, 这样缓冲区的内容就会和相对应的数据文件不一致,这时,缓冲区标识为“脏缓冲区”。
    ● 自由缓冲区(Free Buffer):
             当“脏缓冲区”的内容被写入数据文件后,因为该缓冲区与相应数据文件部分内容 一致,所以将这些缓冲区称为“自由缓冲区”。 当执行SELECT 语句时,会将对应数据文件部分数据读取到数据高速缓存的相应 缓冲区,因为缓冲区与数据块内容完全一致,所以这些缓冲区也被称为“自由缓冲区”。
    ● 忙缓冲区(Pinned Buffer):
    “忙缓冲区”是指服务器进程正在访问的缓冲区。 每个数据库的数据库高速缓冲区大小有限,所以一般不会把磁盘上的所有数据都可以存 放在缓冲区中。 为了防止数据库高速缓冲区空间不够用,Oracle 会将脏缓冲区中的数据写入对应的数 据文件中,以腾出空间给新的数据。 如果缓冲区不脏,即为“自由缓冲区”,它可以直接被读入新的数据块。)随后访问被写 入磁盘导致额外的高速缓存未命中的数据。 “脏列表”的作用就是记录脏缓冲区的情况。如果某些缓冲区中的数据块被修改,就会 加入该列表,只有脏列表中的缓冲区数据块需要写回数据库文件,一旦写回数据库文件,缓 冲区就会从脏列表中清除。

    2) 非标准块大小支持


         数据库块是Oracle 数据库I/O 的最小单位。 每个数据库都可以通过参数DB_BLOCK_SIZE 指定标准块大小(2k、4k、8k、16k、 32k),默认为8k。但是Oracle 数据库同时支持多种块大小,我们称这些标准块以外的块为 “非标准大小块”。 非标准大小块的高速缓冲区大小指定以下参数(DB_nK_CACHE_SIZE):


    • DB_2K_CACHE_SIZE
      DB_4K_CACHE_SIZE
      DB_8K_CACHE_SIZE
      DB_16K_CACHE_SIZE
      DB_32K_CACHE_SIZE
      DB_nK_CACHE_SIZE 参数不能用于设定标准大小块的高速缓存,标准大小块的缓存 尺寸由参数DB_CACHE_SIZE 的值决定。
    3) 使用多个缓冲区


            数据库管理员(DBA )可以通过创建多个缓冲池来提高数据库缓冲区高速缓存的性能。 用户可以指定方案对象(schema object)(表,簇,索引,及分区)使用相应的缓冲池, 以便控制数据被移出缓存区的时机。


    • ● 保留缓冲区(Keep Buffer Cache):用来保留在内存中最有可能重用的对象。 保留这些对象将减少I/O 操作,指定DB_KEEP_CACHE_SIZE 参数的值配置。
      ● 循环缓冲区(Recycle Buffer Cache):用来保留被重用机会不大的内存块。 指定DB_RECYCLE_CACHE_SIZE 参数的值配置该缓冲区的大小。
      ● 默认缓冲区(Default Buffer Cache):此池始终存在。它相当于一个实例的数据库 高速缓冲区中保留和循环区以外的部分。指定参数DB_CACHE_SIZE。
      注:保留缓冲区和循环缓冲区并非默认缓冲区的子集。 可以使用BUFFER_POOL 子句对对象定义默认的缓冲区。
      1 EXAMPLE:
      2 CREATE INDEX cust_idx …
      3 STORAGE (BUFFER_POOL KEEP);
      4 ALTER TABLE oe.customers
      5 STORAGE (BUFFER_POOL RECYCLE);
    4) 数据库高速缓冲区空间管理


            Oracle 用LRU(Least Recently Used)算法来管理数据高速缓冲区。该算法将最近使 用的数据块按照使用时间的早晚排成队列,当缓冲区占满后,调入新的数据块时,必须清除 已有的数据块,来获得空闲数据块空间,那么,最合理的选择就是清除最早没有使用数据块, 因为使用该块的概率相对比较小。通过规划SGA 时合理地设置数据高速缓存的尺寸,尽量 的避免缓冲区占满的情况发生,否则就会降低系统的效率。

             Oracle 将队列分成两端,分别为热端和冷端。

    image

                 假设新数据块K 将要被读入,又没有空闲位置,则Oracle 会对冷端头J 块进行判断: 假设J 被访问次数为1,Oracle 将会认为J 不是一个经常被访问的块(冷),则会将J 块踢出队列,将K 插入至F 的位置,F 和其他块右移:

    image
               假设新数据块L 将要被读入,又没有空闲位置,则Oracle 会对冷端头I 块进行判断: 假设I 被访问次数为5,Oracle 将会认为I 是一个经常被访问的块(热),则会将I 块放 入热端头,A 和其他快右移如下图所示:

    image

           这时,将对下一个冷端头块H 进行判断: 假设H 被访问次数为1,Oracle 将会认为H 不是一个经常被访问的块(冷),则会将H 块踢出队列,将L 插入至E 的位置,E 和其他块右移:
    image
            但是这时,我们会发现。I 块被放入热端头,经过漫长的时间,I 块最终会被放到冷端 头进行判断;如果这段时间中,即使I 没有再被访问过,I 的访问次数也仍然是5,这样I 将再次回到热端头,这样就造成了死循环,I 永远无法被踢出列表。
          因此,当I 被放到热端头的时候,访问次数将会被清零。如果这段时间I 被访问多次,
    则有机会回到热端头,否则,可能被踢出列表。

    5) 数据库高速缓冲区的大小管理
      1 --1> 连接数据库
      2 [oracle@oracle ~ ]$ sqlplus / as sysdba
      3 --2> 显示数据库高速缓冲区的大小
      4 SQL> show parameter db_cache_size
      5 NAME TYPE VALUE
      6 ------------------------------------ ----------- ------------------------------
      7 db_cache_size big integer 0
      8 --3> 显示保留缓冲区的大小
      9 SQL> show parameter db_keep_cache_size
     10 NAME TYPE VALUE
     11 ------------------------------------ ----------- ------------------------------
     12 db_keep_cache_size big integer 0
     13 --4> 显示循环缓冲区的大小
     14 SQL> show parameter db_recycle_cache_size
     15 --5> 修改数据库高速缓冲区的大小
     16 SQL> ALTER SYSTEM SET db_cache_size=500m;
     17 --6> 清空数据库高速缓冲区
     18 SQL> ALTER SYSTEM FLUSH buffer_cache;

    image

    1.2 重做日志缓冲区(Redo Log Buffer)


             当运行Oracle 服务器的时候,如果突然断电或系统瘫痪,会不会造成数据的丢失?
              Oracle 提供了一套复杂的机制来维护数据完整性,并最终确保数据不会丢失。这就是重做 日志文件的作用。 重做日志缓冲区用来记录对数据缓冲区数据进行的修改,可以循环使用。当用户运行 DML(INSERT、UPDATE、DELETE)以及DDL(CREATE、ALTER、DROP)语句时,会改 变数据高速缓存中的相应缓冲区。但是在修改这些缓冲区之前,Oracle 会自动生成重做项, 首先要将这些缓冲区的变化记载到“重做日志缓冲区”中。重做日志缓冲区由一条一条的重 做项构成,每条重做项记载了修改的时间、被修改的块、修改位置以及新数据。缓冲区被循 环使用,当重做日志缓冲区填满时,数据库系统将重做日志缓冲区的内容写入日志文件。在 系统发生故障时,可以通过重做项重新执行对数据库的修改,实现对实例的恢复。 重做日志缓冲区的大小由LOG_BUFFER 初始化参数来决定。

    1.3 共享池(Shared Pool)


            SGA 的共享池(Shared Pool)内包含了库缓存(Library Cache),数据字典缓冲区 (Dictionary Cache),并行执行消息缓冲区(Buffers for parallel execution messages), 以及用于系统控制的各种内存结构。 共享存储区的大小由SHARED_POOL_SIZE 初始化参数来决定。同数据高速缓冲区一
    样,它的大小可以动态的修改。 如图1-5 所示,共享池由库高速缓存和数据字典缓冲区组成。

    image

    1) 数据字典缓冲区


               数据字典是一系列保存了数据库参考信息(例如数据库结构,数据库用户等)的表和视 图。Oracle 需要频繁地使用经过解析的SQL 语句访问数据字典。数据字典信息对Oracle 能否正常运行至关重要。

              数据字典是用来描述数据库数据的组织方式的,由表和视图组成。

              数据字典由脚本$oracle_home/rdbms/admin/catalog.sql 创建。
              视图有三种实例:

                      user_*(当前用户所拥有对象的有关信息),

                      all_*(当前用户可访问对象 的信息),

                      dba_*(数据库中所有对象的信息)。
            在Oracle 数据库中,数据库的一些信息,包括账户、数据文件、表的描述、用户权限 等信息,存储在数据字典表中,数据字典表被存放在SYSTEM 表空间的数据文件中。
           因为执行任何SQL 语句都需要访问数据字典,所以为了提高数据字典的访问性能, Oracle 在共享池中专门为存放数据字典信息分配了内存空间,这些内存空间被称为数据字 典高速缓冲区。用来缓存来自于数据字典的定义。

            例如,当用户执行"SELECT * FROM emp WHERE empno=7788"语句时,Oracle 需要 查询数据字典dba_tables 确定表emp 是否存在;如果该表已经存在,还需要查询数据字典 dba_tab_columns 确定列empno 在表emp 中是否存在,然后才能生成执行语句的过程(执 行计划)。这些定义在首次查询时存入数据字典高速缓冲区,在后续过程中用到就可以直接 使用,而不必重新查询数据字典。

    2) 库高速缓存


           库高速缓冲区又可分为共享SQL 区和共享PL/SQL 区。
           共享SQL 区用来存放最近执行的SQL 语句信息,包括语句文本、解析树及执行计划。 执行计划就是Oracle 为执行特定的SQL 语句,产生的优化的执行步骤。
           库高速缓冲区由许多上下文区(Context Area)组成,SQL 语句和执行计划存放在相应上 下文区中,并且不同SQL 语句分别对应于不同的上下文区。当客户端运行SQL 语句时,服 务器进程首先检查是否存在对应于该SQL 语句的上下文区,若存在,则按照其执行计划直 接执行该SQL 语句;否则生成SQL 语句执行计划,并将执行计划、SQL 语句存放到相应 上下文区中,然后执行该SQL 语句。
           在开发应用程序时,必须要注意使用标准格式来编写SQL 语句,使得SQL 语句尽可能 共享上下文区,以降低SQL 语句解析次数,进而提高应用性能。
           在解析SQL 语句时,认为完全相同的SQL 语句有以下特点:


    • ● 语句文本相同。
      ● 大小写相同。
      ● 赋值变量相同。


           共享PL/SQL 区用来存放最近执行的PL/SQL 语句,解析和编译过的程序单元和过程(函 数、包和触发器)也存放在此区域。
           类似于数据高速缓冲区,Oracle 也是使用LRU 算法来管理库高速缓存的。通过库高速 缓存,可以最小化SQL 语句解析次数,进而提高应用程序的性能。

    1.3 大池(Large Pool)


           数据库管理员可以配置一个称为大型池(Large Pool)的可选内存区域,供一次性大量 的内存分配使用,例如:


    • ● 共享服务器(shared server)及Oracle XA 接口(当一个事务与多个数据库交互 时使用的接口)使用的会话内存(session memory)
      ● I/O 服务进程
      ● Oracle 备份与恢复操作


           如果从大型池内为共享服务器,Oracle XA,或并行查询缓冲区(parallel query buffer) 分配会话内存,共享池(shared pool)就能够专注于为共享SQL 区(shared SQL area) 提供内存,从而避免了共享池可用空间减小而带来的系统性能开销。
           此外,Oracle 备份与恢复操作,I/O 服务进程,及并行执行缓存所需的存储空间通常 为数百KB。与共享池相比,大型池能够更好地满足此类大量内存分配的要求。
            注:大型池不使用LRU 列表管理其中内存的分配与回收。

    1.4 JAVA 池(Java Pool)和流池(Stream Pool)


           SGA 内的Java 池(Java Pool)是供各会话内运行的Java 代码及JVM 内的数据使 用的。Java 池是SGA 的可选区域,用来为Java 命令解析提供内存。只有在安装和使用JAVA 时才需要JAVA 池。
           Java 池的大小由JAVA_POOL_SIZE 初始化参数来决定。
           在数据库中,管理员可以在SGA 内配置一个被称为数据流池(Streams Pool)的内 存池供Oracle 数据流(Stream)分配内存。管理员需要使用TREAMS_POOL_SIZE 初 始化参数设定数据流池的容量(单位为字节)。如果Oracle 数据流第一次使用时系统中没 有定义数据流池,Oracle 将自动地创建一个。
    注:Java 编程和Oracle 数据流的详细讨论超出了本课程的范围。

    2.查询SGA 的大小

      1 --1> 显示SGA 大小
      2 SQL> SHOW SGA
      3 SQL> SELECT * FROM v$sga;
      4 SQL> SHOW PARAMETER sga_target
      5 --2> 显示SGA 最大值
      6 SQL> SHOW PARAMETER sga_max_size

    (二)、程序全局区PGA


                当客户进程访问Oracle 服务器时,会在Oracle 服务器端为用户进程分配相应的服务进 程,并且为该服务进程分配相应的内存空间来存放其数据和控制信息,每一个后台进程也同 样需要为其分配专用的存储空间,这块内存空间被称为程序全局区(Program Global Area 或 Process Global Area )。如图1-5 表示为服务进程专门分配PGA 存储区。
               所有进程都可共享SGA,而PGA 是非共享的。当建立服务进程或后台进程时,Oracle会自动为其分配PGA;而当服务进程或后台进程终止时,Oracle 会自动释放PGA 所占用 的内存空间。
               根据专有和共享服务器模式的不同,PGA 的内存也会发生变化,但大致可分为三部分:

               1.私有SQL 区


            image

       私有SQL 区(Private SQL Area)中包含绑定信 息(Bind Information)及运行时内存结构(Runtime Memory Structure)等数据。每个提交了SQL 语句 的会话都有一个私有SQL 区。每个提交了相同SQL 语句的用户都有自己的私有SQL 区,但她们使用同 一个共享SQL 区(Shared SQL Area)。即多个私有 SQL 区可以和同一个共享SQL 区相联系。 游标的私有SQL 区又可以被分为两个区域,这 两部分的生命周期有所不同:


    • ● 持续数据区(Persistent Area),包含绑定信息(Bind Information)之类的数据。 此区只在游标关闭时才会被释放。
      ● 运行时间区(Run-time Area),当执行请求的第一步创建,对于INSERT, UPDATE,和 DELETE 命令,这个区域当语句执行后释放。 对于查询,只有所有行取出后或查询取消后该区域才释放。 私有SQL 区即可位于SGA 也可位于PGA。
      ● 对专有服务器模式私有SQL 区位于服务进程的PGA 区;
      ● 对共享服务器模式私有SQL 区位于SGA。私有SQL 区管理由用户进程完成,一个用 户进程可以分配的游标私有SQL 区个数由OPEN_CURSORS 参数限定,默认值是50。
      ● 专用服务器:对于每个用户,Oracle 服务器对每个用户进程执行一个专有的服务进程。
      ● 共享服务器:一个共享服务器进程响应多个客户端的请求。



    2.会话内存(Session Memory)


            会话内存是为会话变量和会话有关信息分配的内存。对共享服务器模式,会话内存也是 共享的。

    3.SQL 工作区(SQL Work Areas)


                SQL 工作区用于排序(Sort)哈斯连接(Hash-join)位图(Bitmap)合并和位图创建。 该区域大小即可控制也可以自动调整。 用户可以对工作区的容量进行控制与调优。一般来说,更大的工作区能够显著地提高 SQL 操作的性能,但代价是消耗更多的内存。最理想的情况是,工作区能够容纳SQL 语 句的全部输入数据及额外的控制内存结构。否则语句的响应时间将增加,因为部分输入数据 必须放入临时磁盘区。在极端情况下,如果工作区容量远小于输入数据,那么输入数据需要在临时磁盘区与工作区间多次交换。这将显著地增加SQL 操作的响应时间。
                从Oracle9i 开始,通过设置初始化参数WORKAREA_SIZE_POLICY(默认值为TRUE) 和PGA_AGGREGATE_TARGET,工作区可以自动的进行全局管理。
    PGA_AGGREGATE_TARGET 参数设置实例的PGA 目标总量,可以动态的修改。当 设置了此参数,所有的工作区大小就变为自动修改。
                     在Oracle9i 之前,控制SQL 工作区的最大尺寸,是通过设置以下参数:
    SORT_AREA_SIZE、HASH_AREA_SIZE、BITMAP_MERGE_AREA_SIZE 和
    CREATE_BITMAP_AREA_SIZE 完成的。

    4.专用服务器和共享服务器


    某些内存分配特性与系统使用专用服务器还是共享服务器模式有关。表2-1 展现了两 种情况的区别。

    image

    (三)、自动共享管理内存ASMM

    1.ASMM 的作用


               从Oracle 10g 开始,Oracle 提供了自动SGA 的管理(简称ASMM,Automatic Shared Memory Management)新特性。所谓ASMM,就是指我们不再需要手工设置shared pool、 buffer pool 等若干内存池的大小,而是为SGA 设置一个总的大小尺寸即可。Oracle 数据库 会根据系统负载变化,自动调整各组件的大小,从而使得内存始终能够流向最需要它的地方。 例如,假设某个系统在上午10:00 到下午9:00 属于OLTP 应用,因而可能会用到较
    多的BUFFER CACHE;而下午9:00 到上午10:00 属于OLAP 系统,可能会用到较大 的SHARED POOL 和LARGE POOL。如果我们启用了ASMM,数据库就会根据负载的变 化而自动对内存大小进行调整,就不需要数据库管理员手工进行调整了。

    2.如何使用ASMM


                Oracle 10g 提供了一个新的初始化参数:sga_target 来启动ASMM,该参数定义了整 个SGA 的总容量。同时,初始化参数statistics_level 必须设置为typical 或all 才能启动 ASMM,否则如果设置为basic,则关闭ASMM。
    ASMM 只能自动调整5 个内存池的大小,它们是:shared pool、buffer cache、large pool、 java pool 和stream pool 。我们不再需要设置shared_pool_size 、db_cache_size 、 large_pool_size、java_pool_size、streams_pool_size 这五个初始化参数。而其他的内存 池,比如log buffer、keep buffer cache 等仍然需要DBA 手工进行调整。
                Oracle 10g 还提供了另一个初始化参数sga_max_size。sga_target 的值不能超过 sga_max_size 的值,修改sga_max_size 时,必须重启实例才能生效,而sga_target 则可 以在线修改,立即生效,无须重启实例。

    3.ASMM 的原理


                为了实现ASMM,Oracle 新引入了一个名为MMAN(Memory Manager)的后台进程。 每隔很短的一段时间,MMAN 进程就会启动,然后去询问一下Oracle 提供的各个内存组件 顾问,比如有buffer cache 顾问,也有shared pool 顾问,由这些顾问根据当前的负载情况, 将这5 个可以自动调整的内存池的、建议的大小尺寸,返回给MMAN。于是,MMAN 进程 就会根据该返回的值,来设置各个内存池。同时,如果我们使用了spfile,还会将这些顾问
    得出的建议值写入spfile 里。这样,下次启动实例时,就可以直接把顾问得出的建议值拿来 作为启动内存池的依据了。

                如果我们启用了ASMM,同时又手工设置了可以自动调整大小的内存池的尺寸,比如 设置了参数shared_pool_size 为一个非0 值的时候,会怎么样?对于Oracle 10g 来说,我 们为自动调整大小的内存组件设置了值,则会以我们设置的值作为自动调整的最小值。也就 是说,假设sga_target 为4GB,而我们将shared_pool_size 设置为600MB,则MMAN 在 进行自动调整时,永远不会将shared pool 设置为600MB 以下。

    4.11g 中的ASMM


                在Oracle11g 数据库中,使用自动内存管理特性不再需要设定参数SGA_TARGET 和 PGA_AGGREGATE_TARGET,,因为这两个参数都已经被修改成自动调优的,除非想指定 PGA 和SGA 的最小值才需要设定这两个参数。 在Oracle11g 数据库中,则需要设置一个叫做MEMORY_TARGET 的初始化参数,这 个参数是指整个Oracle 实例所能使用的内存大小,包括PGA 和SGA 的整体大小,在 MEMORY_TARGET 的内存大小之内,PGA 和SGA 所用的内存可以根据当前负载情况自 动相互转换。
                如果当初始设定的MEMORY_TARGET 的内存不够当前数据库使用的时候,Oracle11g 还提供了另外一个初始化参数MEMORY_MAX_TARGET,当原始设定的内存不够使用的时 候,可以手工来动态调节MEMORY_TARGET 的大
    小,但是不允许超过MEMORY_MAX_TARGET 的值。下面这张图简单明了的描述出 了Oracle11g 数据库内存大小的设定参数

    image

    三、Oracle 进程结构


             Oracle 的进程分为用户进程、服务器进程和后台进程。
             用户进程是在客户机内存上运行的程序,在用户请求连接Oracle 数据库时建立。用户 进程不能直接同Oracle 服务器交互,而是把操作请求提交给服务进程。服务进程是在用户 同Oracle 数据库建立会话之后自动在服务器端创建的进程。服务进程直接同Oracle 服务器 交互,执行用户请求并返回结果。服务进程有专有和共享之分。 后台进程属于Oracle 实例的一部分,Oracle 的后台进程的作用是对数据进行操作,并 维护数据库系统,使其始终处于良好的状态。

    (一)、用户进程与服务器进程


              如图3-1 所示,如果用户要提交并执行SQL 语句,在用户的客户机上,要运行一个程 序如SQL*Plus 或其他应用程序,这个程序是作为用户进程(User Process)运行的。当用 户需要连接到Oracle 服务器时,在Oracle 服务器上会启动一个与用户进程对应的进程来为 用户进程服务,这个进程称为服务进程(Server Process)。注意,服务进程总是运行在Oracle 服务器上面的,而用户进程既可以运行在服务器上面,也可以运行在客户机上面。

    image

                 用户进程同服务进程通讯,需要建立一个连接,最简单的情况是用户进程同服务进程位 于同一台机器, 可通过内部进程通讯机制进行连接; 用户也可以通过客户服务器 (Client-Server)软件通过网络进行连接,这时需要进行相应的网络通讯配置;比较复杂的 情况是通过三层结构,比如以浏览器作为客户端进行连接。
                 如果一个客户进程对应一个服务进程,我们称该连接为专有(Dedicated)服务器连接, 如果多个客户进程对应一个服务器进程,我们称该连接为共享(Shared)服务器连接。
                 当特定的用户同Oracle 服务器连接成功,并通过Oracle 服务器的身份验证,这时用户 和Oracle 服务器之间建立了一个会话(Session),同一个用户可以并发的同Oracle 数据库建 立多个会话。当用户退出或异常断开时,会话就结束了。

    (二)、后台进程结构


                      为了实现为多用户提供服务且保证系统性能,在一个多进程Oracle 系统(multiprocess Oracle system)中,存在多个被称为后台进程(background process)的Oracle 进程。
                      一个Oracle 实例中可以包含多种后台进程,这些进程不一定全部出现在实例中。系统 中运行的后台进程数量众多,用户可以通过V$BGPROCESS 视图查询关于后台进程的信 息。这里给大家简单介绍一些经常用到的数据库后台进程:


    • ● 系统监控进程(SMON),主要任务是实例启动时执行恢复性工作,对有故障数据库实例进行恢复。
      ● 进程监控进程(PMON),主要任务用于恢复失败的用户进程。
      ● 数据库写入进程(DBWn),将修改后的数据块内容写回数据文件。
      ● 日志写入进程(LGWR),将内存中的日志内容写入日志文件。
      ● 检查点进程(CKPT),启动检查点,用于减少数据库实例恢复所需要的时间。
      ● 归档进程(ARCn),当数据库服务器以归档方式运行时调用该进程完成日志归档。
      ● 恢复进程(RECO),用于分布式数据库中的失败处理。
      ● 锁进程(LCKn),在并行服务器模式下确保数据的一致性。
      ● 调度进程(Dnnn),负责把用户进程分配到可用的服务进程进行处理。

    1.查看后台进程

      1 SQL> SELECT name FROM v$bgprocess WHERE paddr!='00';
      2 或者
      3 [oracle@oracle ~ ]$ ps -ef | grep ora_

    2.LGWR 日志写入进程(Log Writer)


            LGWR 日志写入进程负责将重做日志缓冲区的日志条目写入磁盘上的联机日志文件。
            当运行DML 或DDL 语句时,服务器进程首先要将事务的变化记载到重做日志缓冲区, 然后才会写入数据高速缓冲区,并且重做日志缓冲区的内容将会被写入联机重做日志文件, 以避免系统出现意外带来的数据损失(如果操作系统断电,内存中的重做日志缓冲区的内容 会丢失,而存在磁盘上的联机日志文件则不会丢失),这项任务由LGWR 来完成。
            重做日志缓冲区是一个循环结构,LGWR 将重做日志缓冲区中的重做记录写入联机重 做日志文件后,相应的缓冲区内容将被清空,保证Oracle 有空闲的重做日志缓冲区可以写 入。


            在出现以下情况时LGWR 会开始工作:


    • ● 在DWBR 进程将脏缓冲区写入数据文件之前。 //预写协议
      ● 在重做日志记录达到缓冲区的三分之一。
      ● 日志缓冲区记录的日志多于1M。
      ● 每隔3 秒钟。 //重做日志缓冲区是循环使用的,要腾出足够的空间给新的记录使用
      ● 提交事务(执行Commit)。 //提交事务相当于确定保存修改,不存入日志文件就有丢失的可能


              Oracle 总是先记载数据变化到重做日志缓冲区,然后才修改数据高速缓存。与之类似, 在后台进程DBWn 将脏缓冲区写入到数据文件之前,首先要由后台进程LGWR 将重做日志 缓冲区写入到重做日志中。与数据高速缓存相比,重做日志缓冲区相对要小得多,但写入频 率高的多,Oracle 必须要确保重做日志缓冲区总有足够的空间容纳新事务,因此每隔3 秒 钟或重做日志缓冲区已有三分之一填满时LGWR 会自动工作。
            另外,Oracle 采用了快速提交机制,当执行COMMIT 操作时,并不是将“脏缓冲区”数 据写入到数据文件中,而是将重做日志缓冲区的内容写入到重做日志文件中,以确保数据库 完整性。此时即使系统出现意外情况(如掉电、系统崩溃等),因为被提交事务已经记载到 了存放在磁盘上的联机重做日志文件中,将来在重新启动数据库时系统会自动进行实例恢 复,并将事务所修改数据写入到数据文件中,从而避免了数据丢失。

    3.DBWn 数据库写入进程(Database Writer)


                 数据库写入进程负责将数据库高速缓冲区(脏缓冲区)的内容写入到数据文件。 尽管有一个数据库写进程( DBW0 )适用于大多数系统,但数据库管理员可以配置额 外的进程(DBW0-DBW9,最多10 个进程),以提高写入性能,通过设置初始化参数 DB_WRITER_PROCESSES 来完成。如果你的系统修改数据严重,这些额外的DBWn 进 程在单处理器系统不是非常有用。
                当 数据库高速缓冲区的块被修改,它被标记为脏缓冲区并添加到以SCN(System Change Number,系统更改号,这里可以看做“时间”)为顺序的LRUW(LRUWriter)列表。 同时,这个顺序与重做日志缓冲区的顺序一致。


    在出现以下情况时DBWn 进程会开始工作:


    • ● 系统发出检查点指令。
    •    //同步数据,详见检查点进程(CKPT)。
      ● 脏缓冲区个数达到指定阀值。
      ● 服务进程搜索一定数目的数据块后,不能找到自由缓冲区。
      ● 数据写入计时时间到。 //客户端执行SELECTINSERTUPDATEDELETE 语句时,都需要访问数据库高 速缓冲区。如果是第一次访问,必须要将数据由数据文件读取到数据库高速缓冲区,所 以Oracle 必须要确保数据高速缓存总是存在足够的“自由缓冲区”以容纳新数据。 当DBWn 进程将脏缓冲区的数据块写入到数据文件后,Oracle 将把“脏缓冲区” 标记为“自由缓冲区”。 因此,为了保证有足够“自由缓冲区”来存放新的数据块,需要DBWn 进程工作。
      ● 表空间脱机或进入只读状态。
      ● 执行删除或截断表操作。
      ● 执行ALTER TABLESPACE … BEGIN BACKUP 命令  //需要同步数据,原理同检查点

    4.CKPT 检查点进程(Checkpoint)


                 CKPT 检查点进程的作用是执行一个“检查点”,同步数据库的所有数据文件、控制文 件和重做日志文件。当执行检查点时,系统促使DBWn 将数据缓冲区中数据的变化写入数 据文件,同时完成对数据文件和控制文件的更新,记录下当前数据库的结构和状态。在执行 一个检查点之后,数据库处于一个完整状态。在数据库发生崩溃后,可以将数据库恢复到上 一个检查点。
                  Oracle 数据库在执行涉及数据变化的语句时,会针对任何修改生成一个顺序递增SCN (System Change Number)值,并且会将SCN 值连同事务的变化一起记载到重做日志缓 冲区。在数据文件、控制文件头部以及重做日志文件中都记载有该值。Oracle 通过比较各 种文件的SCN 值,确定文件是否损坏、系统是否异常,最终确定系统是需要进行实例恢复 还是介质恢复。在发出检查点时,数据文件、控制文件和重做日志的SCN 值完全一致。

    进程CKPT 在以下情况下会开始工作:

    • 发生日志切换 。
    • 关闭实例(SHUTDOWN ABORT 除外)。
       
    • 手工执行检查点操作。
       
    • 由初始化参数LOG_CHECKPOINT_INTERVAL 和 LOG_CHECKPOINT_TIMEOUT 强制发出。


    假设现在有一张员工工资表,员工号7899 的员工张三工资为8000,由于工作表现良好, 现在为其加薪到10000。
    1> 此时,第一次读取,需要将数据从数据文件读入数据库高速缓冲区。

    image

    2> 数据读入数据库高速缓冲区后,我们修改数据的条目先记录入重做日志缓冲区:

    image

    3> 接着新数据被写入数据库高速缓冲区

    image

    4> 提交,LGWR 进程将条目从重做日志缓冲区写入联机重做日志文件

    image

    5> 执行检查点CKPT,同步数据库
    发现联机日志文件为最新,同步更新数据文件、控制文件,触发了DBWn 进程。

    image

    5.SMON 系统监控进程(System Monitor)


               SMON 系统监控进程主要作用是强制对数据库进行恢复操作。在实例启动时,如果上 一次数据库是非正常关闭,并且重做日志文件和控制文件的SCN 值是不同的,Oracle 将自 动在重新打开数据库之前,通过执行重做日志文件的记录,来同步所有数据文件、控制文件 和重做日志文件,确保所有数据库文件的一致性,然后才打开数据库。
                如果检查点进程一例中,第四步完成后发生系统掉电、崩溃,那么数据会不会丢失呢? 当然不会。我们知道,系统掉电,导致内存中的数据(数据库高速缓冲区)的数据丢失。那 么自然上例中的第五步无法完成(无法从数据库高速缓冲区写入数据文件),但是由于此时 已写入联机日志文件。因此,此时数据将从联机日志文件中更新,而更新的数据量是多少, 自然就是由SCN 决定。
               这一过程我们成为“实例恢复”。该过程不需要数据库管理员手工干预,由SMON 进程 自动完成。


         该进程还负责在启动实例时清理临时段和合并区(Extent)碎片等工作。所以SMON 进程的工作归纳如下:

    • ● 进行实例恢复
    •   ● 合并数据文件的自由空间
    • ● 释放数据文件的临时段
            
          
           

    6.PMON 进程监控进程(Process Monitor)


                   PMON 进程监控进程负责对失败的用户进程或服务进程进行恢复。当用户进程连接到 Oracle 服务器时,Oracle 将在服务器端分配相应的服务进程。这时由PMON 进程来监视用 户进程的执行情况。当由于种种原因,用户对Oracle 数据库的连接,发生崩溃、挂起或异 常终止现象时,该进程负责清除服务进程所占用的资源,回滚没有完成的事务。


    当PMON 检测到用户进程失败时,进行的工作归纳如下:

    • ● 回滚当前用户的事务
    • ● 释放当前用户加的表或行级锁
    • ● 释放用户的其他资源
    • ● 重新启动死掉的调度进程


                 假定我们在客户端运行SQL*Plus 并通过网络访问Oracle 服务器,那么Oracle 将在服 务器端分配相应的服务进程。假如用户异常终止SQL*Plus,或出现网络断开或客户端死机 的情况,PMON 就必须检测到这种情况,并释放掉服务进程所占用的资源。

    7.ARCn 归档进程(Archive)


                   ARCn 归档进程完成日志的归档备份,是一个可选后台进程。当数据库运行于归档日志 模式(ARCHIVELOG)下时,负责将已写满的重做日志文件复制到归档日志目标文件中, 这样可以保留所有的重做记录。有了归档日志和数据库早期的物理备份,即使发生磁盘数据 丢失,也可以通过原来的数据文件和归档日志进行恢复。
                    日志文件是循环使用的,当一个日志写满后,自动切换到另一个日志文件。ARCn 进程 在重做日志文件进行切换时开始工作,将写满的重做日志文件写入归档日志文件。根据归档 任务量的大小,通过设置初始化参数,可以同时启动多个ARCn 进程。Oracle 服务器默认 的是非归档模式,通过修改初始化参数文件中的参数可以改变归档模式。生产数据库通常都 会设置为归档模式。

    8.RECO 恢复进程(Recover)


                 RECO 恢复进程负责在分布式数据库(Distributed Database)环境下,自动恢复失败的 分布式事务。当某个分布式事务由于网络连接故障或者其他原因失败时,RECO 进程将会 尝试与该事物相关的所有数据库进行联系,已完成对失败事物的处理工作。

    9.Dnnnn 调度进程


                     Dnnnn 调度进程是多线程服务器体系结构的一部分,调度进程接受用户进程的请求, 将他们放入到请求队列中,然后为请求队列中的用户进程分配一个服务进程。

    四、Oracle 数据库


    数据库是数据存储的容器,作用是用来收集、存储数据和返回信息。 Oracle 数据库的物理结构包括两个部分,分别是数据库文件和操作系统文件。

    (一)、数据库文件

    1.控制文件(Control Files)


               控制文件是一个很小的二进制文件,用于记录和维护数据库结构(数据库名、数据文件 的名称和路径、联机重做日志文件的名称和路径、备份信息等),每个Oracle 数据库可以有 1-8 个控制文件,最少要包含一个控制文件。
              一般情况下,实例和数据库是一一对应的关系,Oracle 数据库通过控制文件在实例和 数据库之间建立关联。
              在启动数据库时,系统会根据初始化参数control_files 来定位控制文件,Oracle 从控制 文件中读取组成数据库所有物理文件的信息,这样就可以打开必要的文件。如果控制文件丢 失或出现错误,数据库的启动就会失败。为了保护数据库的安全,Oracle 采用复合控制文 件,即控制文件同时可以有多个镜像。

    1> 查看控制文件相关信息
      1 SQL> SELECT name FROM v$controlfile;
      2 NAME
      3 ----------------------------------------------------------------
      4 /disk1/orcl/control01.ctl
      5 /disk2/orcl/control02.ctl
      6 /disk3/orcl/control03.ctl
      7 或者
      8 SQL> SHOW PARAMETER CONTROL
    2> 二进制备份控制文件
      1 SQL> ALTER DATABASE BACKUP CONTROLFILE TO ‘路径/文件名’;
    3> 文本备份控制文件
      1 SQL> ALTER DATABASE BACKUP CONTROLFILE TO trace
      2 AS‘路径/文件名’;

    2.数据文件(Data File)


             数据文件是用于存储数据库数据的物理文件,它由若干个操作系统文件组成,在数据文 件中存储着用户数据(表、索引等)、数据字典以及回滚段数据等。
             数据文件和表空间有着密切的关系,Oracle 数据库至少要包含一个数据文件,并且数据 文件是表空间的物理组成元素,一个表空间可以包含多个数据文件,并且每个数据文件只能 惟一地属于某个表空间。“表空间”在后面会做介绍。
    数据文件相关视图:

      1 SQL> DESC V$DATAFILE;
      2 SQL> DESC DBA_DATA_FILES;

    3.联机重做日志文件(Online Redo Log File)


             重做日志文件是用于记录数据库变化的物理文件,其作用是在发生意外时恢复Oracle 数据库。
             日志文件以日志文件组的形式存在。我们知道重做日志缓冲区是循环使用的:当重做日志缓冲区的重做项被写入联机重做日 志文件后,重做日志缓冲区的内容可以被覆盖;
             日志文件也是循环使用的,当联机重做日志文件中记录的数据已经同步写入数据文件, 联机重做日志文件中的内容也可以被覆盖。但是这种循环以组的方式循环。
    如图4-1 所示,当前数据库有三个重做日志组,每个组有两个成员(同一组内多个成员 为镜像关系,成员是为了保护文件不丢失、损坏而作的冗余,一般存放在不同磁盘上)。正 在使用的是第一个日志文件组。当第一个日志文件组写满后切换至第二组写入,第二组写满 后切换至第三个重做日志组,第三个重做日志组写满后,再切换回第一组写入

    image

    (二)、操作系统文件

    1.初始化参数文件(Initialization Parameter File)


              初始化参数文件定义了Oracle 实例的各项参数,初始化参数文件是打开Oracle 实例必 需的文件。对于Linux 操作系统,初始化参数文件一般默认存放在$ORACLE_HOME/dbs 路径下。初始化参数文件分为静态初始化参数文件和动态初始化参数文件。  静态初始化参数文件为文本格式,可以用vi 编辑器编辑,默认名为initsid.ora; 动态初始化参数文件为二进制格式,不可以用vi 编辑器编辑,默认名为spfilesid.ora。 关于初始化参数文件的详细内容,会在《数据库的启动和关闭》章节介绍。

    2.口令文件(Password File)


              口令文件用于存放特权用户信息及其口令的文件,“特权用户”是指具有启动、关闭 Oracle 服务器并建立数据库等特殊权利的用户。如具有SYSDBA、SYSOPER、SYSASM 权限的用户。 关于口令文件的详细内容,会在《管理用户安全性》章节介绍。

    3.归档日志文件(Archive Log File)


              归档日志文件用于记录联机重做日志文件中的内容。 我们知道,联机重做日志文件以日志组的形式存在,并且循环使用。也就是说,如果联 机重做日志文件被覆盖以后数据库发生故障,由于日志已经覆盖,将会导致数据库无法恢复。 因此,我们希望联机重做日志文件能够得以保留。而联机重做日志文件是联机的,也就 是说,数据库正常运行的时候需要用到,这时候如果把日志全部留在联机重做日志文件中, 必然会对数据库的正常运行造成影响;而这些日志往往是出现意外时才会使用的。 由于以上的原因,能够保留联机重做日志文件的内容,同时将这些内容脱机,是最好的 解决方案。归档日志文件正满足了这些需求。
              当联机重做日志组发生切换时,上一组的内容同时写入归档日志文件,这样既可以保证 日志不会丢失,又可以保证不影响数据库的正常运行。 关于归档日志文件的详细内容,会在《日志文件和归档日志文件》章节详细介绍。

    4.追踪文件(Trace File)和警告文件(Alert Log File)


              追踪文件用于存放后台进程和服务器进程的跟踪信息。后台进程跟踪文件存放着后台进 程的警告和错误信息,并且每个后台进程都有相应的跟踪文件。服务器进程跟踪文件用于存 放SQL 语句的跟踪信息该类文件会记载SQL 语句的执行时间、执行计划等信息等。 警告文件由连续的消息和错误组成。通过查看警告文件,可以查看到Oracle 内部错误、 块损坏错误以及非默认的初始化参数值,并且可以监视特权用户的所有操作,例如启动、关 闭数据库。

    五、练习

      1 
      2 1.下列哪个组件不是Oracle 实例的组成部分?
      3 A. 系统全局区SGA B. PMON 后台进程
      4 C. 控制文件D. 调度程序
      5 2.下面哪个内存区域是用来缓存数据字典信息的?
      6 A. Database Buffer Cache B. PGA
      7  C. Redo Log Buffer D. Shared Pool
      8  3.如果一个服务进程中止,Oracle 使用哪一个进程来释放它所占有的资源?
      9 A. DBWn B. LGWR
     10  C. SMON D. PMON
     11  4.下列哪个后台进程,不是Oracle 实例必须启动的?
     12 A. DBWN B. LGWR
     13  C. SMON D. ARCn
     14  5.如果一个数据缓冲区被称为脏缓冲区,正确的说法是:
     15 A. 该缓冲区的数据与数据文件不一致。
     16 B. 该缓冲区的数据与数据文件一致。
     17 C. 该缓冲区的数据正在被使用。
     18 D. 该缓冲区的数据包含有错误。
     19 6.数据库高速缓冲区的大小可以由以下哪个初始化参数决定?
     20 A. LOG_BUFFER
     21  B. LARGE_POOL_SIZE
     22  C. DB_CACHE_SIZE
     23  D. SHARED_POOL_SIZE
     24  7.以下哪种情况会引起检查点?
     25 A. SCN 的值发生变化。
     26 B. 切换日志。
     27 C. 脏缓冲区个数达到指定阀值。
     28 D. 执行Commit 命令。
     29 8.下面哪一个是对LGWR 进程的描述
     30 A.负责对实例进行恢复
     31 B.进程失败后进行清理
     32 C.记录数据库的变化,以便进行数据恢复
     33 D.将脏缓冲区写入数据文件
     34  专业专注超越Oracle 体系结构篇之Oracle 服务器结构
     35 - 29 -
     36 9.下列哪些视图可用来查询控制文件信息:
     37 A.V$CONTROLFILES B.V$DATABASE
     38  C.V$CONTROLFILE D.V$CONTROL
     39  10.LGWR 进程在哪些情况下写重做日志缓冲区到重做日志文件:
     40 A.每三秒
     41 B.当日志缓冲区的1/3 已满时
     42 C.执行Commit 语句时
     43 D.都正确
     44 11.哪种数据库文件用来记录应用程序对数据库的改变:
     45 A.数据文件B.控制文件
     46 C.重做日志文件D.参数文件
     47 12.下面哪些内存区域使用LRU 机制进行管理:
     48 A.JAVA 池B.重做日志缓冲区
     49 C.数据库高速缓冲区D.大池
     50 13.下面哪个后台进程可实现对重做日志文件的归档:
     51 A.PMON B.CKPT
     52  C.LCKn D.ARCn
     53 
     54 
     55 
  • 相关阅读:
    css中的单位
    受控或非受控组件的场景
    JavaScript中的Attribute和Property区别和联系
    git分支管理模型
    flexboxbug
    插入排序
    c# Ef 表达式拼接
    根据实体类生成SQL语句(增删改)
    windows下安装mysql双版本
    【2022.04.19】QQ机器人好用插件——学习群友的聊天的ChatLearning
  • 原文地址:https://www.cnblogs.com/ios9/p/7594721.html
Copyright © 2020-2023  润新知