`

日志系统原理

阅读更多

一:事务系统
1.事务的工作模型
   事务必须满足原子性,所封装的操作或者全做或者全不做。
事务管理系统需要做两件事,1)让日志系统产生日志,2)保证多个事务并发执行,满足ACID特性。
   事务系统工作模型,见图1。



   如图,事务管理管理器控制查询处理器的执行、控制日志系统以及缓冲区。日志在缓冲区生成,日志管理器在一定的时候控制缓冲区的刷盘操作。当系统崩溃的时候,恢复管理器就被激活,检查日志并在必要时利用日志恢复数据。


2.事务的原语操作
在事务系统的运行当中,有三个地址空间供元素存储:1)磁盘空间、2)缓冲区、3)事务的局部地址空间。
一个简单的读、修改X元素操作的流程如:事务到缓冲中读取元素X,如果命中,则读取事务局部地址空间并返回,如果未命中,则先将相关页从磁盘读入缓冲区。事务在它的局部地址空间中修改元素X,然后写入缓冲区,再从缓冲区写入磁盘。当然缓冲区的数据也可能不是立即拷贝入磁盘的,这取决于具体的缓冲区管理策略。
为了便于描述,我们描述了五个操作原语:
1) INPUT(X):将包含数据库元素X的磁盘块拷贝到内存缓冲区
2) READ(X,t):将数据库元素X拷贝到事务的局部变量t。更准确地说,如果包含数据库元素X的块不在内存缓冲区中,则首先执行INPUT(X)。接着将X的值赋给局部变量t。
3) WRITE(X,t):将局部变量t的值拷贝到内存缓冲区中的数据库元素X。更准确地说,如果包含数据库元素X的块不在内存缓冲区中,则首先执行INPUT(X)。将着将t的值拷贝到缓冲区中的X。
4) OUTPUT(X):将包含X的缓冲区拷贝到回磁盘。

3.应用
    假设银行系统数据库中有两个元素,元素A(表示用户1的余额,值为1000,单位:RMB)与元素B(表示用户2的余额,值为500,单位:RMB)。这时候用户1需要向用户2转帐50元。相应的过程为:
   A := A – 50;
   B := B + 50;

   执行之前,两个用户的总余额为1500(1000+500),两个操作执行成功之后总余额还是1500(950+550)。处于一致性状态。
   如果只有前一条执行成功,那总额只为1450(950+50)。处于不一致状态。
   为了避免这种不一致状态,我们需要将这两个操作封装成一个事务T。
   我们将这两个操作分解为原语操作。如下:
   READ(A,t); t := t-50; WRITE(A,t);
   READ(B,t); t := t+50; WRITE(B,t);
   OUTPUT(A); OUTPUT(B)    //这两个OUTPUT原语操作由缓冲区管理器发出。

表1给出了这8个原语操作的执行步骤,给出了每一步中A和B的内存值、磁盘拷贝的值以及事务T地址空间中局部变量t的值

表1: 一个事务的步骤及其对内存和磁盘的影响
步骤 动作 t 内存A 内存B 磁盘A 磁盘B
1 READ(A,t) 1000 1000   1000 500
2 t := t-50 950 1000   1000 500
3 WRITE(A,t) 950 950   1000 500
4 READ(B,t) 500 950 500 1000 500
5 t := t+50 550 950 500 1000 500
6 WRITE(B,t) 550 950 550 1000 500
7 OUTPUT(A) 550 950 550 950 500
8 OUTPUT(B) 550 950 550 950 550

  在表1中不难发现,只要所有的这些步骤都执行成功,数据库的一致性就能得到保持。如果在执行OUTPUT(A)前系统发生了故障,那么磁盘上存储的数据库不会受到任何影响,就好象事务T从来没有发生过一样。但是如果故障在OUTPUT(A)后而在OUTPUT(B)前发生,那么数据就会处于不一致状态(从表中可以看出,磁盘中A为950,B为500)。我们不能防止这种情况的发生,但可以安排当这些情况发生时对问题进行修复-----或者A为1000、B为为500,或者A为950,B为550。


二:undo日志
1.概述
  日志是日志记录的一个序列。在多事务的数据库系统中,每个事务有若干个操作步骤。每个日志记录记载有关某个事务已做的某些情况。几个事务的行为可以是“交错的”,因此可能是一个事务的某个步骤被执行,并且其效果被记录到日志中,接着执行另外一个事务的某个步骤并记入日志,接着可能接着做第一事务的下一个步骤,也可能执行另外一个事务的某个步骤。依次类推。事务的交错执行使日志更复杂,因为仅在事务结束后记载事务的全过程是不够的。
  如果系统崩溃,恢复管理器就被激活,检查日志以重建数据库的一致性状态。恢复时,有些事务的工作将会重做,它们写到数据库的新值会重写一次。而另外一些事务的工作将被撤消,数据库被恢复,将仿佛这些事务从来没执行过一样。
   Undo日志是日志类型的一种,这类日志仅仅进行第二类修复。对于要被撤消的事务,因为不能肯定它对数据库的修改是否已经写到磁盘中,所以对于该事务的所有更新都将被撤消,数据库恢复到事务发生以前的状态。


2.日志记录
  日志只允许以附加的方式写入数据。日志块最初在主存中创建,像数据块一样也由缓冲区管理,在确当的时刻,日志块会从缓冲区写入到磁盘。
   关于undo记录形式有四种:
   1) <START T>: 这一记录表示事务T开始
   2) <COMMIT T>: 事务T已经完成。
   3) <ABORT T>: 事务T不能成功执行。
   4) <T, X, v>: 事务T改变了数据库元素X的值,元素X原来的值为v。

3.undo日志规则
要想让undo日志能使我们从系统故障中恢复,事务必须遵循两条规则。
规则1)如果事务T改变了数据库元素X,那么形如<T, X, v>的日志记录必须在X的新值写到磁盘前写到磁盘
规则 2)如果事务提交,则其COMMIT日志记录必须在事务改变的所有数据库元素已写到磁盘后再写到磁盘,但应尽快。

简单概括,undo日志系统顺序如下:
1) 指明所改变数据库元素的日志记录
2) 改变的数据库元素自身
3) COMMIT日志记录。

4.应用
对于前面所举的例子(A转帐50元给B帐号),如果使用了undo日志系统,则相关的工作流程如表2如下。

表2:undo日志系统的工作原理
步骤 动作 t 内存A 内存B 磁盘A 磁盘B undo日志
1             <START T>
2 READ(A,t) 1000 1000   1000 500  
3 t := t-50 950 1000   1000 500  
4 WRITE(A,t) 950 950   1000 500 <T, A, 1000>
5 READ(B,t) 500 950 500 1000 500  
6 t := t+50 550 950 500 1000 500  
7 WRITE(B,t) 550 950 550 1000 500 <T, B, 500>
8 FLUSH LOG            
9 OUTPUT(A) 550 950 550 950 500  
10 OUTPUT(B) 550 950 550 950 550  
11             <COMMIT T>
12 FLUSH LOG            


    在表2,我们可以看到FLUSH LOG这个命令。该命令的用于强制将还没有刷盘日志记录写到磁盘上。对于步骤8执行之前,三个undo记录(<START T>,<T, A, 1000>,<T, B, 500>)是存储在缓冲区中的(这样描述是为将问题简单化),执行步骤8之后,三条undo记录便写入了磁盘日志文件。在步骤12再次执行FLUSH LOG命令时,只将未刷盘的日志记录<COMMIT T>写入磁盘。
    步骤9、10,在前面已经描述过,是将数据库元素的修改从缓冲区写入到磁盘文件,因为WRITE操作仅仅是将修改反应到缓冲区中(这样描述也是为了将问题简单化)。
    关注步骤8,执行完步骤8之后,将满足undo的规则1(如果事务T改变了数据库元素X,那么形如<T, X, v>的日志记录必须在X的新值写到磁盘前写到磁盘)。在我们真正将数据库元素A与B的修改反应到磁盘前,我们已经将它们对应的<T, A, 1000>与<T, B, 500>写入到磁盘日志文件。
   关注步骤11与步骤12,执行它们将满足undo的规则2(如果事务提交,则其COMMIT日志记录必须在事务改变的所有数据库元素已写到磁盘后再写到磁盘,但应尽快)。我们已经将数据库元素A与B的修改反应到磁盘上,接着可以写入<COMMIT T>来表示事务T的成功执行,但是该日志记录还在缓冲区中,所以我们在步骤12执行FLUSH LOG进行日志刷盘。


5.使用undo日志进行数据库的恢复
    现在假设系统故障发生了。有可能给定事务的某些数据库更新已经写到磁盘上,而同一事务的另外一些更新尚未到达磁盘。如果这样,事务的执行就不是原子的,数据库状态就可能不一致。这时候,我们就有必要使用日志将数据库恢复到一致的状态。
    比如,在表2中,如果故障发生在步骤9之后、步骤10之前。数据库的状态就是不一致的。
   恢复管理的第一个任务就是将事务划分为已经提交事务和未提交事务。如果在日志中<COMMIT T>,根据undo的规则2,事务T所做的全部改变在此之前已经写到磁盘上,因此当故障发生时,该事务T不可能导致数据库的不一致状态。
   然而,假设在日志中,只有<START T>记录,而没有与之相匹配的<COMMIT T>记录。那么就有可能在崩溃前,事务的某些修改已经反应到磁盘上,而另外一些修改可能未发生或者还在缓冲区中。这种情况下,T是一个未完成的事务,因为必须被撤消。也就是说,T所做的任何修改都必须恢复为原来的值。Undo的规则1使该想法可以成为可能,因为在修改数据刷盘之前,日志文件中已经保存了修改数据的原先值。对于<T, X, v>,只需要将X的值恢复为v就行了(我们不必检查数据库中X现有值是否为v)。
  日志中可能有一些未提交的事务,并且甚至可能有一些未提交的事务修改了X,所以恢复时采用从日志文件尾向前扫描。在扫描过程中记住所有有<COMMIT T>或<ABORT T>记录的事务T。同时在随后的扫描中,如果它看见<T, X, v>,则:
1) 如果T的COMMIT记录已被扫描到,则什么也不做。
2) 否则,将数据库中元素X的值改为v。在做完这些操作后,为以前未中止且未完成的每个事务T写入一个日志记录<ABORT T>,然后刷新日志。

对于表2,作如下分析:
1)崩溃在第12步后发生。因为<COMMIT T>日志记录已经写入日志文件。当恢复时,不需要处理T事务。
2)崩溃发生在11步和12步之间。日志中记录了三条记录:<START T>、<T, A, 1000>以及<T, B, 500>。当恢复管理进行向后扫描时,首先遇到记录<T, B, 500>,于是它将B在磁盘上的值存为500。接着它遇到记录<T, A, 1000>,于是它将A在磁盘上的值存为1000。最后记录<ABORT T>被写到日志中且日志被刷新。
3)崩溃发生在第8步和第11步之间。情况2一样。
4)崩溃发生在第8步之前。日志系统中只有一条记录:<START T>。记录<ABORT T>被写到日志中且日志被刷新。(实际上,在实际系统中,可能其他的事务执行FLUSH LOG操作,而将事务T的日志记录写入磁盘中,如果是这样的话,日志中可能已经写入<T, A, 1000>和<T, B, 500>,对于这种情况,执行情况2的恢复就行。实际上这些细节不影响undo的恢复效用,为了简单起见,忽视这些情况)。


6.静态检查点
   正如我们所看到的那样,恢复时需要检查整个日志。当采用undo类型的日志时,一旦<COMMIT T>日志记录被写入日志文件,事务T的日志记录就可以忽视了。但是此时事务T却不能截断日志,因为事务是交替执行的,如果这时将日志截断,可能丢失活动着的事务的日志记录。
解决该问题的方法是周期性地对日志做检查点。步骤如下:
1)停止接受新的事务
2)等待所有当前的活动事务提交或终止,并且在日志文件中写COMMIT或ABORT记录。
3)将日志刷新到磁盘
4)写入日志记录<CKPT>,并再次刷新记录。
5)重新开始接受新事务。
当恢复事,扫描到<CKPT>日志时,就不需要继续扫描日志记录了。

例如:假设日志开始时是这样的:
<START T1>
<T1, A, 5>
<START T2>
<T2, B, 10>

  这时候,我们做一个检查点。等待事务T1和事务T2都完成,才在日志文件中写<CKPT>记录。检查点做完后,可以接受新的事务,这里为事务T3,T3执行了一些操作,此时系统崩溃了。日志文件的内容如下:
<START T1>
<T1, A, 5>
<START T2>
<T2, B, 10>
<T2, C, 15>
<T1, D, 20>
<COMMIT T1>
<COMMIT T2>
<CKPT>
<START T3>
<T3, E, 25>
<T3, F, 30>

   恢复时,从文件尾部开始扫描,因为T3是未完成事务,则将磁盘上F的值存为前值30、磁盘上E的值存为前值25。当扫描到日志记录<CKPT>时,我们知道没有必要检查以前的日志记录了,并且数据库状态的恢复已经完成。


7.非静态检查点
静态检查点的缺点在与,可能需要很长时间等待活跃事务的完成,在用户系统看来似乎是静止了。非静态检查克服了该缺点,在做检查点时允许新事务进入。步骤如下:
1) 写入日志记录<START CKPT(T1,……,Tk)>.其中T1,……,Tk为活跃事务。
2) 等待T1,……,Tk每一个事务提交或终止,但允许其它事务开始。
3) 当T1,……,Tk都已完成时,写入日志记录<END CKPT>并刷新日志。

当系统发生故障时,从日志尾部开始扫描日志。根据扫描过程中先遇到<END CKPT>记录还是<START CKPT(T1,……,Tk)>记录,有两种情况。
1) 如果先遇到<END CKPT>记录。所有未完成的事务在<START CKPT(T1,……,Tk)>记录后开始,只要扫描到<START CKPT(T1,……,Tk)>。就不需要继续扫描了。<START CKPT(T1,……,Tk)>前的记录是可以截断的。
2) 如果先遇到<START CKPT(T1,……,Tk)>,那么崩溃发生在检查点过程中。未完成事务只可能是到达<START CKPT(T1,……,Tk)>前遇到的那些,以及T1,……,Tk在崩溃前未完成的那些。因此只要继续扫描到未完成事务中最早的那个事务的开始就行了。
一个通常的规律是,一旦<END CKPT>记录到了磁盘,我们就可以将上一个<START CKPT>记录前的日志删除了。


例如:假设日志开始时是这样的:
<START T1>
<T1, A, 5>
<START T2>
<T2, B, 10>

    现在我们决定做一非静态检查点。因为事务T1和T2是活动的,我们写入记录<START CKPT(T1,T2)>。在等待T1和T2完成时,事务T3开始了。
    执行到崩溃时,日志记录可能如下(对应恢复的第一种情况):  
<START T1>
<T1, A, 5>
<START T2>
<T2, B, 10>
<START CKPT(T1,T2)>
<T2, C, 15>
<START T3>
<T1, D, 20>
<COMMIT T1>
<T3, E, 25>
<COMMIT T2>
<END CKPT>
<T3, F, 30>

    对于这种情况,恢复时,从尾部开始检查日志。T3是未完成的事务,将磁盘上的F值恢复为值30。发现记录<END CKPT>,知道所有未完成的事务从前一个START CKPT开始。继续扫描,将磁盘上E的值恢复为25。继续扫描,发现没有其它已开始但尚未提交的事务。恢复完成。
    当然,崩溃时的日志记录可能是另外一种格式,如下:
<START T1>
<T1,  A, 5>
<START T2>
<T2, B, 10>
<START CKPT(T1,T2)>
<T2, C, 15>
<START T3>
<T1, D, 20>
<COMMIT T1>
<T3, E, 25>

对于这种情况,崩溃发生在检查点过程中。向后扫描,确定T3然后又确定T2是未完成事务,并撤消它们所做的修改。当到达<START CKPT(T1,T2)>时,我们知道其它可能未完成的事务只有T1了,但是T1的<COMMIT T1>记录已经发现。我们也发现了<START T3>。所以我们只要继续扫描到记录<START T2>,并在此过程中将磁盘B的值恢复成10。





三:redo记录
1.概述
   Undo是恢复的一种策略,但是不是唯一的策略。
   Undo日志的一个潜在的问题是:在所有修改数据没有写到磁盘前,不允许提交该事务。有时,让修改的数据页暂时缓冲在主存中,是可以节省磁盘IO的;只要在崩溃的时候有日志用来恢复。

2.redo日志的规则
   Redo日志用新值表示数据元素的更新,而undo日志使用的是旧值。Redo日志的<T,X,v>表示:事务T为数据库元素X写入新值v。
   Redo日志系统的规则只有一条:
   规则1:在修改磁盘上任何数据库元素X之前,要保证所有与X这一修改相关的日志记录,包括更新记录<T,X,v>以及<COMMIT T>记录,都必须出现在磁盘上。
   Redo日志顺序如下:
1) 指出被修改元素的日志记录
2) COMMIT日志记录
3) 改变的数据元素自身。

3.应用
对于前面所举的例子(A转帐50元给B帐号),如果使用了undo日志系统,则相关的工作流程如表3如下。
表3:redo日志系统的工作原理
步骤 动作 t 内存A 内存B 磁盘A 磁盘B undo日志
1             <START T>
2 READ(A,t) 1000 1000   1000 500  
3 t := t-50 950 1000   1000 500  
4 WRITE(A,t) 950 950   1000 500 <T, A, 950>
5 READ(B,t) 500 950 500 1000 500  
6 t := t+50 550 950 500 1000 500  
7 WRITE(B,t) 550 950 550 1000 500 <T, B, 550>
8             <COMMIT T>
9 FLUSH LOG            
10 OUTPUT(A) 550 950 550 950 500  
11 OUTPUT(B) 550 950 550 950 550  

   <T, A, 950>与<T, B, 550>记录的是新值。只有所有相关的日志记录被刷盘(步骤9),才允许将A与B的新值写到磁盘。步骤10与11在日志刷盘之后立即将数据修改写到磁盘,而实际中为了提高IO命中率,刷盘的时间可能会晚些。

4.使用redo日志的恢复
  对于redo日志,根据规则1,我们可以知道如果日志没有<COMMIT T记录>,则事务T的修改所做的所有的更新都没反映到磁盘上,就像事务T从来没有发生过一样。
如果发现记录<COMMIT T>记录,却不敢保证所有的数据库修改已经反映到磁盘上,这和undo日志是相反的。我们必须将事务T重做一次。
使用redo日志恢复,过程如下:
1) 确定提交事务
2) 从首部开始扫描日志,对遇到的每一<T,X,v>记录:
     (a):如果T是未提交的事务,则什么也不做。
      (b):如果T是提交的任务,则为磁盘上数据库元素写入值v
3)对于每一个未完成的事务T,在日志中写入一个<ABORT T>记录并刷新日志。

例如,对于表3
1) 如果故障发生在第9步以后,<COMMIT T>记录已被刷新到磁盘。恢复时,遇到记录<T, A, 950>,为磁盘上的A写入值950。遇到记录<T, B, 550>,为磁盘上的B写入值550。
2) 如果故障发生在第9步之前,如果<COMMIT T>已经到达磁盘,则恢复过程同情况1。如果<COMMIT T>未到达磁盘,T被做为未完成事务,磁盘上的A和B不作任何修改,最后将一条<ABORT T>写入日志并刷盘。



  • 大小: 8 KB
分享到:
评论

相关推荐

    深入理解数据库日志系统原理.doc

    深入理解数据库日志系统原理

    日志类vc代码

    在编程领域,日志记录是调试、监控和分析软件运行状态的重要工具。"日志类vc代码" 提供了一个简单的实现,使得C++程序...通过学习和理解这个代码,可以提升对C++类设计、文件操作、日志系统原理以及多线程同步的理解。

    oracle重做日志原理

    Oracle的重做日志是数据库管理系统中至关重要的组成部分,它主要负责记录数据库中所有事务对数据所做的更改。重做日志的主要目标是确保数据的安全性和一致性,即使在面临硬件故障或系统崩溃时也能通过恢复机制恢复到...

    简单易用的日志系统

    在IT行业中,日志系统是不可或缺的组成部分,它主要用于记录应用程序运行时的详细信息,帮助开发者追踪错误、调试代码以及优化系统性能。本项目“简单易用的日志系统”是基于Tomcat服务器和SLF4J(Simple Logging ...

    网络日志系统在嵌入式文件上的实现

    ### 网络日志系统在嵌入式文件上的实现 #### 概述 随着信息技术的进步,嵌入式系统在各个行业中发挥着越来越重要的作用。然而,由于嵌入式环境的特殊性,尤其是在非正常断电的情况下,如何保护数据安全成为了一个...

    C++ 开源日志系统

    **C++ 开源日志系统** C++ 是一种强大的编程语言,广泛应用于各种软件开发,尤其是在需要高性能和系统级操作的场景中。在C++中,日志系统扮演着重要的角色,它允许开发者记录程序运行过程中的信息,帮助调试、性能...

    日志监控系统源码

    《日志监控系统源码解析》 在信息技术领域,日志监控系统扮演着至关重要的角色。通过对系统产生的日志数据进行实时分析,可以洞察系统的运行...深入理解并掌握这类系统的实现原理,对于提升IT运维水平具有重大意义。

    中级高级软考!! PDF 数据库系统原理

    数据库系统原理是信息技术领域中的核心课程,尤其对于中级和高级软件考试来说,深入理解数据库系统的概念、设计与管理是至关重要的。本资料“中级高级软考!! PDF 数据库系统原理”旨在帮助考生全面掌握数据库系统的...

    自己实现的Qt日志记录系统

    通过这种方式,你就可以实现一个既能够将日志记录到文件,又能在Qt界面中实时显示的自定义日志系统。当然,这个系统可以根据实际需求进行扩展,例如添加日志级别过滤、日志滚动、时间戳格式化等功能,以提高日志管理...

    04735数据库系统原理历年真题【至201910】.zip

    04735《数据库系统原理》历年真题集是一个重要的学习资源,它包含了自考试开始以来至2019年10月的所有试题,旨在帮助考生深入理解和掌握数据库系统的概念、设计、实现以及管理等方面的知识。这个压缩包中的文件很...

    04735数据库系统原理(真题及答案)200710至201710.zip

    《04735数据库系统原理》是一门重要的高等教育自学考试科目,主要涵盖了数据库系统的基础理论、设计方法以及实际应用等内容。这个压缩包文件“04735数据库系统原理(真题及答案)200710至201710.zip”包含了从2007年...

    办公日志系统

    在本文中,我们将深入探讨一个基于ASP(Active Server Pages)和SQL(Structured Query Language)技术构建的办公日志系统,了解其核心功能、设计原理以及实施步骤。 一、ASP技术详解 ASP,全称Active Server ...

    SQL操作日志系统(asp)

    本文将深入探讨如何使用ASP(Active Server Pages)来构建一个SQL操作日志系统,支持SQL Server和Oracle两大主流数据库。我们将讨论ASP的基础知识、数据库连接、SQL操作日志的实现原理以及具体的代码实现。 首先,...

    系统日志导出.zip

    在这个场景下,它可能包含如何运行批处理脚本、脚本的工作原理、可能的输出位置以及如何解读日志文件等内容。确保正确理解并按照readMe.txt中的指示操作,可以避免误操作,有效利用导出的日志信息。 导出系统日志...

    数据库系统原理及应用教程PPT课件

    在本套《数据库系统原理及应用教程》PPT课件中,特别聚焦于SQL Server 2000这一经典的数据库管理系统。SQL Server 2000是由微软公司开发的一款关系型数据库管理软件,它在21世纪初广泛应用于企业和组织,以其强大的...

    操作系统原理试题及答案(7份)

    本资料包包含的是关于操作系统原理的七份试题及其答案,旨在帮助学习者深入理解操作系统的核心概念、设计原则以及实现机制。下面将详细解析这些知识点: 1. **操作系统基本概念**:试题可能会涵盖操作系统的基本...

    04735自考数据库系统原理考前串讲

    【04735自考数据库系统原理】是高等教育自学考试计算机科学与技术专业的一门重要课程,旨在帮助考生深入理解和掌握数据库系统的基础理论、设计方法以及应用技术。这门课程的内容涵盖了数据库系统的基本概念、数据...

    数据库系统原理课后答案

    数据库系统原理是计算机科学中的核心课程之一,主要探讨如何有效地存储、管理和检索数据。这份"数据库系统原理课后答案"提供了对课程作业和习题的详尽解答,可以帮助学习者深入理解和掌握数据库系统的概念、设计与...

    web日志的入侵检测,Apache日志恶意行为检测系统

    Apache日志恶意行为检测系统(使用正则的方式或是根据Get,Post参数?=获取后面的值进行匹配检测是否是恶意行为) 前端框架:html + css + jquery + echart 后端框架:flask + python + mysql truncate table ...

    数据库系统原理PPT课件

    数据库系统原理是计算机科学中的核心课程,主要探讨如何有效地存储、管理和检索数据。这门学科源于二十世纪六十年代末,随着计算机技术的发展,数据库技术逐渐成为数据管理的关键技术,对于现代社会的信息处理和决策...

Global site tag (gtag.js) - Google Analytics