Oracle数据库特种恢复技术(一)—原理篇

简介:

oracle数据库特种恢复技术(二)—块内篇链接:http://www.itpub.net/thread-1507766-1-1.html
oracle数据库特种恢复技术(三)—转换篇链接:http://www.itpub.net/thread-1507774-1-1.html
Oracle数据库特种恢复技术(四)—实验篇链接:http://www.itpub.net/thread-1510202-1-1.html

oracle数据库特种恢复技术(五)--redo篇链接:http://www.itpub.net/thread-1517926-1-1.html


      由一次ora-00600 [4000]想到的:

      项目测试库出现过一次由于异常断电引起的ora-00600 [4000]造成数据库无法启动的故障,诊断为系统回滚段数据字典(字典而不是回滚段本身)损坏,且该库处于非归档模式,无任何备份。后使用bbed修改 system表空间数据文件,将相关数据块的事物状态手工修改为一致性状态,数据库得以打开。

由 此引发另一种思路:如果数据库无法使用rman、冷备份、dg、ogg、dsg等方式正常恢复打开,那么还能否找回其中数据?考虑到业务数据还存在于存储 设备上(只是完整性有一些缺失),能否在不启动数据库的情况下,由应用从存储设备上直接读出数据库中的业务数据?为解决以上问题,使用dba一些日常工具 结合oracle数据库内部结构的知识,来分析oracle数据库定位数据的原理。这样做的目的是最终能够通过编写代码(java或者c++)实现半自动 或全自动的数据库无法mount状态下的恢复。


     定位数据的方式:

    Oracle数据库服务进程在定位某行数据时,采用了数据字典+段头extent map的“集中—分散”存储模式,具体为:

              1、在数据字典中存放数据段(对应表或分区)头的物理位置

              2、在数据段头存放段中所有extent(即连续的block集合)的物理位置

      首先从数据字典入手,oracle数据库在创建时会生成一系列的基础数据字典表,这些基础字典记录了一个数据库的物理架构,oracle正是通过这些基 础字典,将逻辑层的表与物理层的存储进行转换和映射。其实建库时运行的catalog.sql等脚本只是在这些基础数据字典上建立了相关的视图、同义词等 (还有一些内存表的映射视图),因此catalog.sql脚本所建立的“字典”存在与否都不会影响数据库的正常运行,但基础数据字典一旦出现异常,数据 库就无法启动。

oracle记录段头物理位置的字典视图是sys.dba_segments,其中记录了段头所在的文件号及在该文件中的块号,其中文件号对应文件物理位置在控制文件中查询。对于sys.dba_segments视图,最值得关心或者说与本文关系最为密切的是:

select a.owner,a.segment_name,a.header_file,a.header_block fromdba_segments a;这四个字段,通过这四个字段就可以定位某用户的某个段的段头在哪个文件的哪个数据块内。通过plsql developer等工具查看sys.dba_segments视图的定义(sqlplus也可,但作为一个成熟的dba,熟练掌握各种工具的特点,完成 不同任务,最大幅度减少达成目标所需的工作量,是一项非常重要的素质),可见其数据来源于sys.sys_dba_segs视图:

create or replace view sys.dba_segments as

select owner, segment_name, partition_name,segment_type, tablespace_name,

      header_file, header_block,

      decode(bitand(segment_flags, 131072), 131072, blocks,

          (decode(bitand(segment_flags,1),1,

           dbms_space_admin.segment_number_blocks(tablespace_id, relative_fno,

           header_block, segment_type_id, buffer_pool_id, segment_flags,

           segment_objd, blocks), blocks)))*blocksize,

      decode(bitand(segment_flags, 131072), 131072, blocks,

          (decode(bitand(segment_flags,1),1,

           dbms_space_admin.segment_number_blocks(tablespace_id, relative_fno,

           header_block, segment_type_id, buffer_pool_id, segment_flags,

           segment_objd, blocks), blocks))),

      decode(bitand(segment_flags, 131072), 131072, extents,

          (decode(bitand(segment_flags,1),1,

          dbms_space_admin.segment_number_extents(tablespace_id, relative_fno,

          header_block, segment_type_id, buffer_pool_id, segment_flags,

          segment_objd, extents) , extents))),

      initial_extent, next_extent, min_extents, max_extents, pct_increase,

      freelists, freelist_groups, relative_fno,

      decode(buffer_pool_id, 0, 'DEFAULT', 1, 'KEEP', 2, 'RECYCLE', NULL)

    fromsys_dba_segs;

   

    继续观察sys.sys_dba_segs视图的定义,

create or replace view sys.sys_dba_segs

(owner, segment_name, partition_name,segment_type, segment_type_id, tablespace_id, tablespace_name, blocksize,header_file, header_block, bytes, blocks, extents, initial_extent, next_extent,min_extents, max_extents, pct_increase, freelists, freelist_groups, relative_fno,buffer_pool_id, segment_flags, segment_objd)

as

select NVL(u.name, 'SYS'), o.name, o.subname,

      so.object_type, s.type#,

      ts.ts#, ts.name, ts.blocksize,

      f.file#, s.block#,

      s.blocks * ts.blocksize, s.blocks, s.extents,

       s.iniexts * ts.blocksize,

      decode(bitand(ts.flags, 3), 1, to_number(NULL),

                                      s.extsize* ts.blocksize),

      s.minexts, s.maxexts,

      decode(bitand(ts.flags, 3), 1, to_number(NULL),

                                      s.extpct),

      decode(bitand(ts.flags, 32), 32, to_number(NULL),

             decode(s.lists, 0, 1, s.lists)),

      decode(bitand(ts.flags, 32), 32, to_number(NULL),

             decode(s.groups, 0, 1, s.groups)),

      s.file#, s.cachehint, NVL(s.spare1,0), o.dataobj#

from sys.user$ u, sys.obj$ o, sys.ts$ ts,sys.sys_objects so, sys.seg$ s,

    sys.file$ f

where s.file# = so.header_file

  ands.block# = so.header_block

  ands.ts# = so.ts_number

  ands.ts# = ts.ts#

  ando.obj# = so.object_id

  ando.owner# = u.user# (+)

  ands.type# = so.segment_type_id

  ando.type# = so.object_type_id

  ands.ts# = f.ts#

  ands.file# = f.relfile#

union all

select NVL(u.name, 'SYS'), un.name, NULL,

      decode(s.type#, 1, 'ROLLBACK', 10, 'TYPE2 UNDO'), s.type#,

      ts.ts#, ts.name, ts.blocksize, f.file#, s.block#,

      s.blocks * ts.blocksize, s.blocks, s.extents,

      s.iniexts * ts.blocksize, s.extsize * ts.blocksize, s.minexts,

      s.maxexts, s.extpct,

      decode(bitand(ts.flags, 32), 32, to_number(NULL),

        decode(s.lists, 0, 1, s.lists)),

      decode(bitand(ts.flags, 32), 32, to_number(NULL),

        decode(s.groups, 0, 1, s.groups)),

      s.file#, s.cachehint, NVL(s.spare1,0), un.us#

from sys.user$ u, sys.ts$ ts, sys.undo$ un,sys.seg$ s, sys.file$ f

where s.file# = un.file#

  ands.block# = un.block#

  ands.ts# = un.ts#

  ands.ts# = ts.ts#

  ands.user# = u.user# (+)

  ands.type# in (1, 10)

  andun.status$ != 1

  andun.ts# = f.ts#

  andun.file# = f.relfile#

union all

select NVL(u.name, 'SYS'), to_char(f.file#) ||'.' || to_char(s.block#), NULL,

      decode(s.type#, 2, 'DEFERRED ROLLBACK', 3, 'TEMPORARY',

                      4, 'CACHE', 9, 'SPACEHEADER', 'UNDEFINED'), s.type#,

      ts.ts#, ts.name, ts.blocksize,

      f.file#, s.block#,

      s.blocks * ts.blocksize, s.blocks, s.extents,

      s.iniexts * ts.blocksize,

      decode(bitand(ts.flags, 3), 1, to_number(NULL),

                                      s.extsize* ts.blocksize),

      s.minexts, s.maxexts,

      decode(bitand(ts.flags, 3), 1, to_number(NULL),

                                     s.extpct),

      decode(bitand(ts.flags, 32), 32, to_number(NULL),

        decode(s.lists, 0, 1, s.lists)),

      decode(bitand(ts.flags, 32), 32, to_number(NULL),

        decode(s.groups, 0, 1, s.groups)),

      s.file#, s.cachehint, NVL(s.spare1,0), s.hwmincr

from sys.user$ u, sys.ts$ ts, sys.seg$ s,sys.file$ f

where s.ts# = ts.ts#

  ands.user# = u.user# (+)

  ands.type# not in (1, 5, 6, 8, 10)

  ands.ts# = f.ts#

      and s.file# = f.relfile#;

    可见其是由三个sql语句块联合而成,而其中2、3语句块明显是回滚段等segment,因此着重观察第一语句块,

select NVL(u.name, 'SYS'), o.name, o.subname,

      so.object_type, s.type#,

      ts.ts#, ts.name, ts.blocksize,

      f.file#, s.block#,

      s.blocks * ts.blocksize, s.blocks, s.extents,

       s.iniexts * ts.blocksize,

      decode(bitand(ts.flags, 3), 1, to_number(NULL),

                                      s.extsize* ts.blocksize),

      s.minexts, s.maxexts,

      decode(bitand(ts.flags, 3), 1, to_number(NULL),

                                      s.extpct),

      decode(bitand(ts.flags, 32), 32, to_number(NULL),

             decode(s.lists, 0, 1, s.lists)),

      decode(bitand(ts.flags, 32), 32, to_number(NULL),

             decode(s.groups, 0, 1, s.groups)),

      s.file#, s.cachehint, NVL(s.spare1,0), o.dataobj#

from sys.user$ u, sys.obj$ o, sys.ts$ ts,sys.sys_objects so, sys.seg$ s,

    sys.file$ f

where s.file# = so.header_file

  ands.block# = so.header_block

  ands.ts# = so.ts_number

  ands.ts# = ts.ts#

  ando.obj# = so.object_id

  ando.owner# = u.user# (+)

  ands.type# = so.segment_type_id

  ando.type# = so.object_type_id

  ands.ts# = f.ts#

      and s.file# = f.relfile#

可 见其数据来源为sys.user$ u, sys.obj$ o, sys.ts$ ts, sys.sys_objects so, sys.seg$s,sys.file$ f这几个基础系统字典表(这些已经都是物理存在的表而不是视图了),而其中最重要的owner、segment_name、header_file、 header_block字段,则分别来自sys.user$、sys.obj$、sys.seg$、sys.file$等字典表,也就是说要在不启动数 据库的情况下定位到用户数据,必须先定位以上几个基本数据字典。那么这几个表的物理存储位置又在哪里呢?或者说在数据库不启动的情况下,如何获得这四个表 的物理存储位置?要回答这个问题,先要了解一下oracle启动的过程,oracle在启动时会加载这些基础数据字典表,而这些字典表的create语句 都写在sys.bootstrap$表里,

SQL> select *from sys.bootstrap$ a where a.sql_text like '%SEG$%';

     LINE#      OBJ# SQL_TEXT

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

        14         14 CREATE TABLE SEG$("FILE#"NUMBER NOT NULL,"BLOCK#" NUMBER NOT NULL,"TYPE#" NUMBE

      而sys.bootstrap$表的物理位置其实是间接“写死”在oracle代码中的。

      在每一个oracle数据库(以10g为例,其他版本相差不大)的system表空间的第一个文件的header block中都按固定结构存放有kcvfh这个结构体,其定义可以通过bbed工具查看。

这 里简单介绍一下bbed工具。Bbed是oracle公司提供的一个文本编辑工具(有些dba把bbed想象的非常神秘高深,其实它就是一个普通的文本编 辑器,用其他文本编辑工具(比如UltraEdit)的宏功能其实可以实现bbed的绝大部分功能),通过它可以方便的查看oracle数据在物理层面的 存储结构。

      为安全考虑,先把数据库system表空间的第一个文件拷贝到另外的目录以便观察。在sqlplus中执行select file#||' '||name||' '||bytes from v$datafile ;将结果中的路径和文件名修改为先前数据文件的拷贝,用来生成bbed列表文件,这样用bbed打开的就是这份复制文件,而不是真正的库中的文件。 Bbed安装编译过程不再赘述。

    进入bbed后默认偏移量是1,即file header block,

    BBED> map

     File:/home/oracle/vie.pdf (1)

     Block: 1                                     Dba:0x00400001

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

     DataFile Header

   

     struct kcvfh, 676 bytes                    @0      

   

     ub4tailchk                               @8188  

    Map命令会自动检测当前数据块的type,并显示该类型数据块内所包含的数据结构信息(这也就是为什么设置不同偏移量,map输出的数据结构信息也会不 同),这里看到,system表空间的file header block中包含一个名为kcvfh的结构体,该结构体占用676 bytes,深入观察:

    BBED> p kcvfh

    struct kcvfh, 676 bytes                     @0      

      struct kcvfhbfh, 20 bytes               @0      

          ub1type_kcbh                         @0        0x0b

         ub1 frmt_kcbh                        @1        0xa2

         ub1 spare1_kcbh                      @2        0x00

         ub1 spare2_kcbh                      @3        0x00

         ub4 rdba_kcbh                         @4        0x00400001

         ub4 bas_kcbh                         @8        0x00000000

         ub2 wrp_kcbh                         @12       0x0000

         ub1 seq_kcbh                         @14       0x01

         ub1 flg_kcbh                          @15       0x04 (KCBHFCKV)

         ub2 chkval_kcbh                      @16       0x2671

         ub2 spare3_kcbh                      @18       0x0000

      struct kcvfhhdr, 76 bytes               @20      

         ub4 kccfhswv                          @20       0x00000000

         ub4 kccfhcvn                         @24       0x0a200100

         ub4 kccfhdbi                         @28       0xea382478

         text kccfhdbn[0]                     @32      P

         text kccfhdbn[1]                      @33      H

         text kccfhdbn[2]                     @34      O

         text kccfhdbn[3]                     @35      N

         text kccfhdbn[4]                     @36      E

         text kccfhdbn[5]                     @37      D

         text kccfhdbn[6]                     @38      B

         text kccfhdbn[7]                     @39      

         ub4 kccfhcsq                         @40       0x00001ae3

         ub4 kccfhfsz                         @44       0x00017c00

         s_blkz kccfhbsz                      @48       0x00

         ub2 kccfhfno                         @52       0x0001

         ub2 kccfhtyp                         @54       0x0003

         ub4 kccfhacid                        @56       0x00000000

         ub4 kccfhcks                          @60       0x00000000

         text kccfhtag[0]                     @64      

         text kccfhtag[1]                     @65      

         text kccfhtag[2]                     @66      

         text kccfhtag[3]                     @67      

         text kccfhtag[4]                     @68      

         text kccfhtag[5]                     @69      

         text kccfhtag[6]                     @70      

         text kccfhtag[7]                     @71      

         text kccfhtag[8]                      @72      

         text kccfhtag[9]                     @73      

         text kccfhtag[10]                    @74      

         text kccfhtag[11]                    @75      

         text kccfhtag[12]                    @76      

         text kccfhtag[13]                    @77      

         text kccfhtag[14]                    @78      

         text kccfhtag[15]                    @79      

         text kccfhtag[16]                    @80      

         text kccfhtag[17]                     @81      

         text kccfhtag[18]                    @82      

         text kccfhtag[19]                    @83      

         text kccfhtag[20]                    @84      

         text kccfhtag[21]                    @85      

         text kccfhtag[22]                    @86      

         text kccfhtag[23]                    @87      

         text kccfhtag[24]                    @88      

         text kccfhtag[25]                    @89      

         text kccfhtag[26]                     @90      

         text kccfhtag[27]                    @91      

         text kccfhtag[28]                    @92      

         text kccfhtag[29]                    @93      

         text kccfhtag[30]                    @94      

         text kccfhtag[31]                    @95      

       ub4kcvfhrdb                            @96       0x00400179

      struct kcvfhcrs, 8 bytes                @100     

         ub4 kscnbas                          @100      0x00000008

    。。。。。。。。。。。。。。。。。

    省略后面内容

    这里的 ub4 kcvfhrdb  @96 0x00400179就是oracle启动过程中非常关键的变量:root dba,root dba中记录了sys.bootstrap$的物理位置,因此可以把root dba理解为指向sys.bootstrap$的指针。这里该变量的值为0x00400179,是按照dba(datablock address)的格式存放,将其转换为二进制后的前10位为文件号,后22位为块号 0x00400179转换为二进制0000 0000 0100 0000 0000 0001 0111 1001,文件号部分:0000 0000 01,块号部分:00 0000 0000 0001 01111001,表示1号文件的377号块,这个数字不是我的库的巧合,而是10g版本数据库的固定位置,在oracle 10g数据库启动时会默认在该偏移量的位置寻找指向sys.bootstrap$表的指针。在11g中sys.bootstrap$的位置改到了 0x00400208。而10g、11g该指针的位置(即rootdba变量自己的位置)是相同的,都是file header block的96偏移量位置。需要注意的是这里的偏移量96,并不是从文件头部开始计算:oracle在建立datafile时,实际生成的文件会比管理 员输入的文件尺寸大一个db_block_size的大小,比如某数据库的db_block_size=8k,建立表空间时指定某文件大小为100M,那 么该文件的实际大小是100M+8K,这8K放在文件的最前面,称作os header block,root dba(也就是指向sys.bootstrap$的指针)的物理偏移量是db_block_size+96。这也就是为什么在一些使用裸设备的库上,在把 裸设备交给oracle的时候,不能把文件尺寸写的和设备的尺寸完全一样,而要稍小一点,否则创建时会出现ORA-27042错误。

    这里有一个小技巧,在使用bbed分析数据文件结构时,要观察在不同主机上的不同版本的库,有些库的数据文件很大,复制、查看不便,可以针对需要,使用 dd命令截取重点关注的部分,比如我在观察11g的file header block时使用 ddif=/u01/app/oracle/oradata/mydb/system01.dbf of=/home/oracle/lt140_header2.tracecount=1000 skip=0 bs=8192截取该文件的前8M数据,生成一个文件,然后传输到主要用来做分析工作的主机上,修改bbed配置文件指向该文件就能用bbed对其进行分 析,这么做还有一个原因就是11g是不带bbed工具的,需要从10g复制源文件再进行编译链接。结果导向、尽量简化中间操作过程是dba工作的重要指导 原则。

通过root dba的了解,已经可以在不打开数据库的情况下定位到sys.bootstrap$表的物理位置,下一个问题是,sys.bootstrap$表里存储的 只是基础字典表的创建语句,那么这些表的物理存储位置是如何确定的?进一步观察sys.bootstrap$表的sql_text字段,以file$为 例:SQL> set line 2000

SQL> set headoff

SQL> select *from sys.bootstrap$ a where a.obj# = 17;


        17         17

CREATE TABLEFILE$("FILE#" NUMBER NOT NULL,"STATUS$" NUMBER NOT NULL,"BLOCKS"NUMBER NOT NULL,"TS#" NUMBER,"RELFILE#"NUMBER,"MAXEXTEND" NUMBER,"INC" NUMBER,"CRSCNWRP"NUMBER,"CRSCNBAS" NUMBER,"OWNERINSTANCE"VARCHAR2(30),"SPARE1" NUMBER,"SPARE2"NUMBER,"SPARE3" VARCHAR2(1000),"SPARE4" DATE) PCTFREE 10PCTUSED 40 INITRANS 1 MAXTRANS 255 STORAGE ( INITIAL 64K NEXT 1024K MINEXTENTS 1 MAXEXTENTS 2147483645 PCTINCREASE 0OBJNO 17 EXTENTS (FILE 1 BLOCK 113))

这个create语句的storage 子句中有两个不常见的参数OBJNO 17 和EXTENTS (FILE 1 BLOCK 113),前者是指定该segment的对象号,而extents是直接指定segment的segmentheader block存储位置,我多次尝试在客户发起的sql语句中使用这两个参数未能成功,判断为只有在数据库处于特定状态或以特定身份运行时才能使用这些参数。 就是说这两个参数直接指定了段在存储上得物理位置。从该参数已经可以直接定位其所对应的数据字典段头的物理位置,而oracle的段头中的extents map结构体就包涵了该段全部的数据块的物理地址。

    在sys.bootstrap$表中记录的数据字典表的create语句,还有一部分是没有直接指定存储位置的,这些表都是某个簇的成员,其实际数据存储在簇中,比如SYS.TAB$:

    CREATE TABLE TAB$("OBJ#" NUMBERNOT NULL,"DATAOBJ#" NUMBER,"TS#" NUMBER NOTNULL,"FILE#"

。。。。。。。。。。。。。。。。。。。。省略。。。。。。。。。。。。。。。。。。。。。。。。。。。。

NUMBER,"SPARE2"NUMBER,"SPARE3" NUMBER,"SPARE4"VARCHAR2(1000),"SPARE5" VARCHAR2(1000),"SPARE6" DATE)STORAGE (  OBJNO 4 TABNO 1) CLUSTERC_OBJ#(OBJ#)

    红色字体白色背景的就是指定SYS.TAB$表加入到C_OBJ#簇中。


至此,在关闭数据库的情况下定位数据块的大致思路已经清晰,总结如下:

1、在system 表空间的第一个数据文件的特定偏移位置,找到root dba变量

2、root dba变量的值就是指向sys.bootstrap$表的物理位置的指针

3、sys.bootstrap$表中记录了数据库基础字典表的物理位置

4、基础字典表内记录了用户段段头的物理存储位置

下一步就是要通过某个段的segmentheader block从数据文件中直接读出表的数据。










本文转自 hsbxxl 51CTO博客,原文链接:http://blog.51cto.com/hsbxxl/768877,如需转载请自行联系原作者

目录
相关文章
|
27天前
|
数据库 索引
深入探索数据库索引技术:回表与索引下推解析
【10月更文挑战第15天】在数据库查询优化的领域中,回表和索引下推是两个核心概念,它们对于提高查询性能至关重要。本文将详细解释这两个术语,并探讨它们在数据库操作中的作用和影响。
46 3
|
26天前
|
负载均衡 网络协议 数据库
选择适合自己的数据库多实例负载均衡技术
【10月更文挑战第23天】选择适合自己的数据库多实例负载均衡技术需要全面考虑多种因素。通过深入的分析和评估,结合自身的实际情况,能够做出明智的决策,为数据库系统的高效运行提供有力保障。
108 61
|
25天前
|
SQL Oracle 关系型数据库
Oracle数据库优化方法
【10月更文挑战第25天】Oracle数据库优化方法
33 7
|
25天前
|
Oracle 关系型数据库 数据库
oracle数据库技巧
【10月更文挑战第25天】oracle数据库技巧
26 6
|
25天前
|
存储 Oracle 关系型数据库
Oracle数据库优化策略
【10月更文挑战第25天】Oracle数据库优化策略
21 5
|
24天前
|
SQL Java 数据库连接
在Java应用中,数据库访问常成为性能瓶颈。连接池技术通过预建立并复用数据库连接,有效减少连接开销,提升访问效率
在Java应用中,数据库访问常成为性能瓶颈。连接池技术通过预建立并复用数据库连接,有效减少连接开销,提升访问效率。本文介绍了连接池的工作原理、优势及实现方法,并提供了HikariCP的示例代码。
39 3
|
24天前
|
存储 Java 关系型数据库
在Java开发中,数据库连接是应用与数据交互的关键环节。本文通过案例分析,深入探讨Java连接池的原理与最佳实践
在Java开发中,数据库连接是应用与数据交互的关键环节。本文通过案例分析,深入探讨Java连接池的原理与最佳实践,包括连接创建、分配、复用和释放等操作,并通过电商应用实例展示了如何选择合适的连接池库(如HikariCP)和配置参数,实现高效、稳定的数据库连接管理。
44 2
|
26天前
|
缓存 负载均衡 监控
数据库多实例的负载均衡技术深入
【10月更文挑战第23天】数据库多实例负载均衡技术是确保数据库系统高效运行的重要手段。通过合理选择负载均衡策略、实时监控实例状态、不断优化调整,能够实现资源的最优分配和系统性能的提升。在实际应用中,需要根据具体情况灵活运用各种负载均衡技术,并结合其他相关技术,以满足不断变化的业务需求。
|
26天前
|
Java 数据库连接 数据库
优化之路:Java连接池技术助力数据库性能飞跃
在Java应用开发中,数据库操作常成为性能瓶颈。频繁的数据库连接建立和断开增加了系统开销,导致性能下降。本文通过问题解答形式,深入探讨Java连接池技术如何通过复用数据库连接,显著减少连接开销,提升系统性能。文章详细介绍了连接池的优势、选择标准、使用方法及优化策略,帮助开发者实现数据库性能的飞跃。
27 4
|
24天前
|
Java 数据库连接 数据库
深入探讨Java连接池技术如何通过复用数据库连接、减少连接建立和断开的开销,从而显著提升系统性能
在Java应用开发中,数据库操作常成为性能瓶颈。本文通过问题解答形式,深入探讨Java连接池技术如何通过复用数据库连接、减少连接建立和断开的开销,从而显著提升系统性能。文章介绍了连接池的优势、选择和使用方法,以及优化配置的技巧。
22 1

推荐镜像

更多
下一篇
无影云桌面