Oracle数据库总结(一)——基础知识
1.1 三大范式精讲
(1)第一范式
在任何一个关系数据库中,第一范式(1NF)是对关系模式的基本要求,不满足第一范式(1NF)的数据库就不是关系数据库。 所谓第一范式(1NF)是指数据库表的每一列都是不可分割的基本数据项,同一列中不能有多个值,即实体中的某个属性不能有多个值或者不能有重复的属性。如果出现重复的属性,就可能需要定义一个新的实体,新的实体由重复的属性构成,新实体与原实体之间为一对多关系。在第一范式(1NF)中表的每一行只包含一个实例的信息。
简而言之,第一范式就是无重复的列。
(2)第二范式
第二范式(2NF)是在第一范式(1NF)的基础上建立起来的,即满足第二范式(2NF)必须先满足第一范式(1NF)。第二范式(2NF)要求数据库表中的每个实例或行必须可以被惟一地区分。为实现区分通常需要为表加上一个列,以存储各个实例的惟一标识。这个惟一属性列被称为主关键字或主键、主码。 第二范式(2NF)要求实体的属性完全依赖于主关键字。所谓完全依赖是指不能存在仅依赖主关键字一部分的属性,如果存在,那么这个属性和主关键字的这一部分应该分离出来形成一个新的实体,新实体与原实体之间是一对多的关系。为实现区分通常需要为表加上一个列,以存储各个实例的惟一标识。
简而言之,第二范式就是非主属性非部分依赖于主关键字。
(3)第三范式
满足第三范式(3NF)必须先满足第二范式(2NF)。简而言之,第三范式(3NF)要求一个数据库表中不包含已在其它表中已包含的非主关键字信息。 例如,存在一个部门信息表,其中每个部门有部门编号(dept_id)、部门名称、部门简介等信息。那么在员工信息表中列出部门编号后就不能再将部门名称、部门简介等与部门有关的信息再加入员工信息表中。如果不存在部门信息表,则根据第三范式(3NF)也应该构建它,否则就会有大量的数据冗余**。
简而言之,第三范式就是属性不依赖于其它非主属性。
1.2 数据库三大范式精要总结
①简单归纳:
-
第一范式(1NF):字段不可分;
-
第二范式(2NF):有主键,非主键字段依赖主键;
-
第三范式(3NF):非主键字段不能相互依赖。
②解释:
-
1NF:原子性。 字段不可再分,否则就不是关系数据库;
-
2NF:唯一性 。一个表只说明一个事物;
-
3NF:每列都与主键有直接关系,不存在传递依赖。
2.Oracle事务
2.1 事务的概念
在数据库中事务是工作的逻辑任务,一个事务是由一个或一组SQL语句组成,通过事务机制确保这一组SQL语句的操作要么全部成功执行成功,完成整个工作任务,如果任何一条SQL执行失败,对数据表所做的操作全部撤销。
事务的目的就是为了保证数据的完整性,以银行转换业务为例:
A用户向B转账1000元,步骤如下:
1)A用户账户余额减少1000元;
2)B用户账户余额增加1000元;
3)记录A用户的交易日志。
4)记录B用户的交易日志。
一笔转帐交易将产生四次数据库操作,要么全部都成功,如果有一个失败,其它操作也应该全部被撤销。
转账交易有四次数据库操作,即四条SQL语句,第一条SQL执行的时候是事务的开始,如果全部的SQL语句执行成功,提交事务,如果有任何一条SQL语句执行失败,回滚事务。
commit; -- 提交事务
rollback; --回滚事务
2.2 事务的特征
对一组SQL语句操作构成的事务,数据库系统必须保证这些操作的原子性、一致性、隔离性和持久性,即ACID原则。
(1)原子性(Atomicity)
事务的原子性是指事务中包含的所有操作要么全做,要么不做,也就是说所有的操作在数据库中要么全部生效,要么全部不生效。
(2)一致性(Consistency)
事务的一致性是指数据库在事务操作前和事务处理后,数据的状态都是一致的。
(3)隔离性(Isolation)
隔离性是指事务所做的修改必须与其他事务所作的修改隔离,数据库允许多个并发的事务同时对数据进行读写或修改,即使出现了SQL语句叉操作数据的情况,也不会导致数据不一致。事务查看数据时数据所处的状态,要么是另一并发事务修改了它之前的状态,要么是另一事务修改它之后的状态。
(4)持久性(Durability)
事务的持久性是指在事务处理结束后,它对数据的修改应该是永久的。通俗的理解就是事务结束后立即把数据写入硬盘中。
2.3 事务的状态
对数据库进行操作的事务有5种状态,如图所示:
(1)活动状态。事务在执行时的状态叫活动状态。
(2)部分提交状态。事务中最后一条语句被执行后的状态叫部分提交状态,事务虽然已经完成,但由于实际输出可能在内存中,在事务成功前还可能发生硬件故障等其他故障,有时不得不进入中止状态。
(3)失败状态。事务不能正常进行的状态叫失败状态,导致失败状态发生的可能原因有硬件原因或逻辑错误,这样事务必须回滚,就进入了中止状态。
(4)提交状态。事务在部分提交后,将往硬盘上写入数据,最后一条信息写入后的状态叫提交状态,进入提交状态的事务就成功完成了。
(5)中止状态。事务回滚,并且数据库已经恢复到事务开始执行前的状态。
2.4 操作事务
Oracle11g中的事务是隐式自动开始的,它不需要用户显示的执行开始事务语句。但对于事务的结束处理,则需要用户进行指定的操作,通常在以下情况,Oracle任务一个事务结束了。
1)执行COMMIT语句提交事务
2)指定ROLLBACK语句撤销事务
3)执行一条数据定义语句,比如create、drop或者alter语句。如果该语句执行成功,那么Oracle系统会自动执行COMMIT命令,否则Oracle系统会自动执行ROLLBACK命令。
4)执行一个数据控制命令,比如grant、revoke等命令,操作执行完毕后,Oracle系统会自动执COMMIT命令。
5)正常地断开数据库的连接、退出SQL环境等。
(1)设置事务
Oracle可以将事务设置成只读事务或者读写事务,如:
set transaction read only; set transaction read write;
Oracle还可以为事务分配回滚段。
(2)提交事务(commit语句)
提交事务是指把数据库进行的全部操作持久性的保存到数据库中,通常用commit语句来完成。事务的提交方式包括如下3种
1)显式提交:使用commit命令使当前事务生效。
2)自动提交:在SQL *Plus里执行“set autocommit on;”命令。
3)隐式提交:除了显式提交之外的提交,如发出DDL命令、程序中止和关闭数据库等。
(3)回滚事务(rollback语句)
回滚事务是指撤销数据库进行的全部操作,Oracle利用回退段来存储修改前的数据。
(4)设置回退点
回退点又称保存点,利用保存点可以将事务划分为若干小部分,这样可以不用回滚整个事务,可以回滚到指定的保存点,有更大的灵活性。如:
savepoint sp01;
rollback to sp01;
2.5 事务特性的保证
(1)保证原子性
主要是利用 Innodb 的undo log。 undo log名为回滚日志,是实现原子性的关键,当事务回滚时能够撤销所有已经成功执行的 SQL语句,他需要记录你要回滚的相应日志信息。 例如
-
当你delete一条数据的时候,就需要记录这条数据的信息,回滚的时候,insert这条旧数据
-
当你update一条数据的时候,就需要记录之前的旧值,回滚的时候,根据旧值执行update操作
-
当年insert一条数据的时候,就需要这条记录的主键,回滚的时候,根据主键执行delete操作
undo log记录了这些回滚需要的信息,当事务执行失败或调用了rollback,导致事务需要回滚,便可以利用undo log中的信息将数据回滚到修改之前的样子。
(2)保证一致性
分为两个层面来说:
-
从数据库层面,数据库通过原子性、隔离性、持久性来保证一致性。也就是说ACID四大特性之中,C(一致性)是目的,A(原子性)、I(隔离性)、D(持久性)是手段,是为了保证一致性,数据库提供的手段。数据库必须要实现AID三大特性,才有可能实现一致性。例如,原子性无法保证,显然一致性也无法保证。
-
从应用层面,通过代码判断数据库数据是否有效,然后决定回滚还是提交数据!
(3)保证隔离性
主要有两种方法,一种是上锁,另一种是并发控制。
(4)保证持久性
主要是利用Innodb的redo log。重写日志, 正如之前说的,MySQL是先把磁盘上的数据加载到内存中,在内存中对数据进行修改,再写回到磁盘上。如果此时突然宕机,内存中的数据就会丢失。 怎么解决这个问题? 简单啊,事务提交前直接把数据写入磁盘就行啊。 这么做有什么问题?
-
只修改一个页面里的一个字节,就要将整个页面刷入磁盘,太浪费资源了。毕竟一个页面16kb大小,你只改其中一点点东西,就要将16kb的内容刷入磁盘,听着也不合理。
-
毕竟一个事务里的SQL可能牵涉到多个数据页的修改,而这些数据页可能不是相邻的,也就是属于随机IO。显然操作随机IO,速度会比较慢。
于是,决定采用redo log解决上面的问题。当做数据修改的时候,不仅在内存中操作,还会在redo log中记录这次操作。当事务提交的时候,会将redo log日志进行刷盘(redo log一部分在内存中,一部分在磁盘上)。当数据库宕机重启的时候,会将redo log中的内容恢复到数据库中,再根据undo log和binlog内容决定回滚数据还是提交数据。
2.6 如果无法保证事务特性会怎么样
以从A账户转账到B账户为例进行说明
(1)无法保证原子性
会出现数据不一致的情形,A账户减去50元,而B账户增加50元操作失败。系统将无故丢失50元
(2)无法保证一致性
-
例一:A账户有200元,转账300元出去,此时A账户余额为-100元。你自然就发现了此时数据是不一致的,为什么呢?因为你定义了一个状态,余额这列必须大于0。
-
例二:A账户200元,转账50元给B账户,A账户的钱扣了,但是B账户因为各种意外,余额并没有增加。你也知道此时数据是不一致的,为什么呢?因为你定义了一个状态,要求A+B的余额必须不变。
(3)无法保证隔离性
假设A账户有200元,B账户0元。A账户往B账户转账两次,金额为50元,分别在两个事务中执行。如果无法保证隔离性,A可能就会出现扣款两次的情形,而B只加款一次,凭空消失了50元,依然出现了数据不一致的情形!
(4)无法保证持久性
为了解决CPU和磁盘速度不一致问题,Oracle是将磁盘上的数据加载到内存,对内存进行操作,然后再回写磁盘。好,假设此时宕机了,在内存中修改的数据全部丢失了,持久性就无法保证。
设想一下,系统提示你转账成功。但是你发现金额没有发生任何改变,此时数据出现了不合法的数据状态,我们将这种状态认为是数据不一致的情形。
3.Oracle join多表查询
连接(join)分为五种,内连接(inner join),左连接(left join),右连接(right join)、全连接(full join)和笛卡儿乘积(cross join),常用的是内连接和左连接。
(1)内连接(inner join)
列出多个表或记录连接字段的匹配记录,即A表和B表的交集。
内连接有三种写法,这三种写法的效果相同:
select * from table1 inner join table2 on table1.id = table2.id;
select * from table1 join table2 on table1.id = table2.id;
select * from table1, table2 where table1.id = table2.id;
(2)左连接(left join)
列出左边(A)表全部的,及右边(B)表符合条件的,不符合条件的以空值代替,记录总数为A表的记录数。
左连接有两种写法,这两种写法的效果相同:
select * from table1 left join table2 on table1.id = table2.id;
select * from table1, table2 where table1.id = table2.id(+);
(3)右连接(right join)
列出右边(B)表全部的,及左边(A)表符合条件的,不符合条件的以空值代替,记录总数为B表的记录数。
右连接有两种写法,这两种写法的效果相同:
select * from table1 right join table2 on table1.id = table2.id;
select * from table1, table2 where table1.id(+)= table2.id;
(4)全连接(full join)
列出右边(B)表全部的,及左边(A)表全部的,不符合条件的以空值代替,记录总数为A表与B表的记录数之和再减相交的记录数。
select * from table1 full join table2 on table1.id = table2.id;
(5)笛卡尔乘积(cross join)
笛卡儿乘积连接,不加任何条件,记录总数为两个表相乘。
笛卡儿乘积连接有两种写法,这两种写法的效果相同。
select * from table1 cross join table2;
select * from table1, table2;
(6)特殊情况
如果要查询某个表中不存在于另一个表中的数据,比如A表不存在于B表中的数据,则不能直接用关联,而应该用not in或者not exists,例:
select * from table1 where table1.id not in (select table2.id from table2);
select * from table1 where not exists (select * from table2 where table1.id = table2.id);
注意:如果表中有多行id一样的数据,那么exists和内关联查出来的数据数量会不一样( 内关联查出的数据会多一些),因为只要exists查出来存在,可能就不会继续查id相同的数据,因为已经存在了。
select count(*) from table1, table2 where table1.id = table2.id
--不等于
select count(*) from table1 where exists (select * from table2 where table1.id = table2.id)
4.Oracle存储过程和函数
(1)创建存储过程
1)存储过程的概念
存储过程是一种命名的PL/SQL程序块,它既可以没有参数,也可以有若干输入、输出参数,通常没有返回值。它不可以被SQL语句直接执行或者调用,只能通过EXECUT命令执行或者PL/SQL程序块内部被调用。由于存储过程是已经编译好的代码,所以其执行效率非常高。
语法规则如下:
--创建
create [or replace] procedure pro_name[(parameter1, parameter2, ......)] is | as
begin
plsql_sentences;
[exception]
[dowith_sentences];
end [pro_name];
--删除
drop procedure pro_name
2)存储过程的参数
存储过程可以接受多个参数,参数模式包括IN、OUT、和IN OUT三种,IN模式参数要求输入,它是根据被传入的值为存储过程提供数据,OUT模式参数只能等待被复制,但是可以存储并输出,IN OUT模式参数可以兼顾前面两种模式的特点。
(2)创建函数
--创建
create [or replace] function fun_name[(parameter1, parameter2, ......)] return date_type is | as
[inner_variable]
begin
plsql_sentences;
[exception]
[dowith_sentences];
end [pro_name];
--调用
declare
test number;
begin
test := fun_name(parameter1, parameter2, .....);
dbms_output.put_line(test);
end;
/
--删除
drop function pro_name;
4.Oracle常见日期计算问题
to_char(trunc(to_date('20100530', 'YYYYMMDD')+1), 'YYYYMMDD')
to_char(add_months(trunc(to_date('20100530', 'YYYYMMDD')), -1), 'YYYYMMDD')
to_char(trunc( to_date('20200530','yyyymmdd'), 'mm'),'yyyymmdd')
to_char(trunc(add_months(to_date('20100530','yyyymmdd'), -1), 'mm'),'yyyymmdd')
select TO_NUMBER(TO_DATE('20100501','yyyymmdd')- TO_DATE('20100429','yyyymmdd')) from dual
5.Oracle游标
(1)游标的概念
游标是用来存储多条查询数据的一种数据结构(结果集),它有一个指针,可以从上往下移动,从而能够遍历每条记录。游标可以通过牺牲内存的方式,大大提高sql语句的执行效率。
(2)基本语法
主要分为4步,如下所示:
-- 测试基础数据
create table stu_info (
id char(5),
name varchar2(10),
sex varchar2(2)
);
insert into stu_info(id, name, sex) values (10001, '小红', '女');
insert into stu_info(id, name, sex) values (10002, '小明', '男');
commit;
--游标语法
declare
-- 1 声明游标
cursor cur_stu_info is
select * from stu_info;
v_stu_info cur_stu_info%rowtype;
begin
-- 2 开启游标
open cur_stu_info;
-- 3 获取数据(一次获取一行)
fetch cur_stu_info
into v_stu_info;
dbms_output.put_line(v_stu_info.id || ' : ' || v_stu_info.name);
-- 4 关闭游标
close cur_stu_info;
end;
游标属性如下所示:
属性 | 返回值类型 | 作用 |
---|---|---|
sql%isopen | 布尔型 | 判断游标是否 '开启' |
sql%found | 布尔型 | 判断游标是否 '获取' 到值 |
sql%notfound | 布尔型 | 判断游标是否 '没有获取' 到值(常用于 "退出循环") |
sql%rowcount | 整型 | '当前' 成功执行的数据行数(非 "总记录数") |
6.Oracle索引
如果数据库性能较低或执行SQL语句的效率很低,最主要原因应该是索引设计不合理或使用索引不合理。
6.1 索引的概念
索引,即表的索引,它对表的重要性就像目录对书一样重要,索引可以大幅提升数据查找的效率。
(1)索引的特点
1)索引是表的一部分,是可选的,表可以没有索引,就像书可以没有目录一样,数据库不做强制要求。
2)合理的索引可以提高数据查找的效率,减少磁盘I/O。
3)唯一索引可以约束表数据的完整性。
4)索引也是数据,需要存储空间。
5)数据库自动维护索引的内容,当对表的记录进行了增、删、改的操作时,数据库会自动更新索引。
6)索引虽然可以提高数据查找的效率,但对表进行增、删、改操作时,数据库除了维护表中的数据,还要维护索引,所以,索引会让增、删、改操作的效率下降。
7)索引提升了数据查找的效率,降低了数据操作的效率,在设计表和索引的时候,要根据实际情况在两者之间权衡。
(2)索引的分类
索引可分为普通索引、唯一索引、反向键索引、位图索引和基于函数的索引,常用的是普通索引和唯一索引,其它三种极少使用。
6.2 索引的基本操作
(1)普通索引
普通索引是默认的索引,也叫B树索引,保存排序过的索引列和对应的rowid值
1)说明
-
oracle中最常用的索引;B树索引就是一颗二叉树;叶子节点(双向链表)包含索引列和指向表中每个匹配行的ROWID值
-
所有叶子节点具有相同的深度,因而不管查询条件怎样,查询速度基本相同
-
能够适应精确查询、模糊查询和比较查询
2)分类
UNIQUE、NON-UNIQUE(默认)、REVERSE KEY(数据列中的数据是反向存储的)
3)创建例子
create index 索引名 on table(field1, field2...)
4)适合场景
列基数(列不重复值的个数)大时适合使用B数索引
(2)唯一索引
唯一索引是B树索引的一种,主要用于保证该列数据的唯一性,创建方式如下
create unique index 索引名 on table(field1, field2...)
注意:如果表中一个字段或多个字段组合起来的值是唯一的,就可以作为表的主键,在创建或修改表时用primay key关键字来指定主键。一个表只能有一个主键,而且组成主键的每个字段值都不能为空。创建主键示例如下:
create table Student
{
id char(4)
name varchar2(20)
score number(3)
primary key(id) --指定id为表的主键
}
不要以表的主键创建索引,因为Oracle自动为表的主键字段创建唯一索引。如果索引建立在多个列上,只有它的第一个列被where子句引用时,优化器才会使用该索引,即至少要包含组合索引的第一列
(3)位图索引
1)说明:
创建位图索引时,oracle会扫描整张表,并为索引列的每个取值建立一个位图(位图中,对表中每一行使用一位(bit,0或者1)来标识该行是否包含该位图的索引列的取值,如果为1,表示对应的rowid所在的记录包含该位图索引列值),最后通过位图索引中的映射函数完成位到行的ROWID的转换
2)创建例子
create bitmap index 索引名 on table(field1, field2...)
3)适合场景
对于基数小的列适合简历位图索引(例如性别等)
(4)索引的存储空间
每个数据库用户有一个缺省表空间,创建的表、主键和索引存放在缺省表空间中,也可以指定其它的表空间。
在实际应用中,会把表和索引的存储空间分开到不同的表空间,减少磁盘的竞争,提升I/O的性能
指定索引的表空间的语法如下:
create index 索引名......tablespace 表空间名
6.3 索引使用总结
(1)索引建立原则总结
-
如果有两个或者以上的索引,其中有一个唯一性索引,而其他是非唯一,这种情况下oracle将使用唯一性索引而完全忽略非唯一性索引
-
至少要包含组合索引的第一列(即如果索引建立在多个列上,只有它的第一个列被where子句引用时,优化器才会使用该索引)
-
小表不要建立索引
-
对于基数大的列适合建立B树索引,对于基数小的列适合简历位图索引
-
列中有很多空值,但经常查询该列上非空记录时应该建立索引
-
经常进行连接查询的列应该创建索引
-
在经常使用在where子句中的字段,应该创建索引
-
索引应该建在选择性高的字段上
-
使用create index时要将最常查询的列放在最前面
-
LONG(可变长字符串数据,最长2G)和LONG RAW(可变长二进制数据,最长2G)列不能创建索引
-
限制表中索引的数量(创建索引耗费时间,并且随数据量的增大而增大;索引会占用物理空间;当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,降低了数据的维护速度)
(2)索引使用注意事项
1)通配符在搜索词首出现时,oracle不能使用索引,例如:
--我们在name上创建索引;
create index index_name on student('name');
--下面的方式oracle不适用name索引
select * from student where name like '%wish%';
--如果通配符出现在字符串的其他位置时,优化器能够利用索引;如下:
select * from student where name like 'wish%';
2)不要在索引列上使用 not 和 <> 操作,可以采用其他方式代替如下:(oracle碰到not或<>会停止使用索引,而采用全表扫描)
select * from student where not (score=100);
select * from student where score <> 100;
--替换为
select * from student where score>100 or score <100
select * from student where score is not null;
4)不要在列上使用函数,这将导致索引失效而进行全表扫描。
7.Oracle锁
数据可使用一种或多种方法实现使用的并发性,这些方法包括保证由事务独占使用表的锁定机制,允许事务串行化的时间戳方法,以及基于验证的事务调度。锁定方法称为悲观方法,因为它们假定事务将破坏串行调度,时间戳和验证方法称为乐观方法。Oracle的锁防止试图访问相同资源的事务之间的破坏性交互。
7.1 并发控制
1.脏读、不可重复读和幻读
(1)未确认的相关性(脏读)
如果一个事务读取了另一个事务尚未提交的更新,则称为脏读。
(2)不一致的分析(不可重复读)
当事务多次访问同一行数据,并且每次读取的数据不同(因为其他事务在更改该数据),将会发生不一致的分析的问题。强调的是 update 和delete ,只需要锁住满足条件的记录即可。
(3)幻象读
一个事务读取了另外一个事务插入的数据,强调的是 insert ,要锁住满足条件及相近的记录。
2.数据库隔离级别
数据库隔离级别可以分为如下:
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
READ-UNCOMMITTED 未提交读 | √ | √ | √ |
READ-COMMITTED 提交读 | × | √ | √ |
REPEATABLE-READ 重复读 | × | × | √ |
SERIALIZABLE 可串行化读 | × | × | × |
其中:
(1)未提交读,事务中发生了修改,即使没有提交,其他事务也是可见的,比如对于一个数A原来50修改为100,但是我还没有提交修改,另一个事务看到这个修改,而这个时候原事务发生了回滚,这时候A还是50,但是另一个事务看到的A是100。可能会导致脏读、幻读或不可重复读。
(2)提交读,对于一个事务从开始直到提交之前,所做的任何修改是其他事务不可见的,举例就是对于一个数A原来是50,然后提交修改成100,这个时候另一个事务在A提交修改之前,读取的A是50,刚读取完,A就被修改成100,这个时候另一个事务再进行读取发现A就突然变成100了;可以阻止脏读,但是幻读或不可重复读仍有可能发生。
(3)重复读,就是对一个记录读取多次的记录是相同的,比如对于一个数A读取的话一直是A,前后两次读取的A是一致的;可以阻止脏读和不可重复读,但幻读仍有可能发生。
(4)可串行化读,在并发情况下,和串行化的读取的结果是一致的,没有什么不同,比如不会发生脏读和幻读;该级别可以防止脏读、不可重复读以及幻读。
MYSQL 中默认的隔离级别是可重复读,可解决脏读和不可重复读的问题。但是不能解决幻读的问题。 Oracle 默认的是Read Commit 读已提交,可以避免脏读的问题。
3.MVCC机制
MVCC 的英文全称是 Multiversion Concurrency Control ,中文意思是多版本并发控制技术。原理是,通过数据行的多个版本管理来实现数据库的并发控制,简单来说就是保存数据的历史版本,可以通过比较版本号决定数据是否显示出来,读取数据的时候不需要加锁可以保证事务的隔离效果。
MVCC解决了:
-
读写之间阻塞的问题,通过 MVCC 可以让读写互相不阻塞,读不相互阻塞,写不阻塞读,这样可以提升数据并发处理能力。
-
降低了死锁的概率,这个是因为 MVCC 采用了乐观锁的方式,读取数据时,不需要加锁,写操作,只需要锁定必要的行。
-
解决了一致性读的问题,当我们朝向某个数据库在时间点的快照是,只能看到这个时间点之前事务提交更新的结果,不能看到时间点之后事务提交的更新结果。
7.2 加锁
(1)锁的概念
让查询和处理操作并发执行,互不干扰,同时修改数据的事务则按照一定的算法调度,这样能既不破坏数据的一致性,又能大大提高数据库的执行效率,这种机制就是锁。锁就是事务可以对数据库资源进行操作的权限,事务要执行,必须先申请对该资源的锁,按照获得的锁的不同,就能够对资源进行相应的锁赋予的操作。Oracle提供行级锁定。
(2)锁的类型
1)按照权限划分
-
排他锁:又称为X锁或写锁。若事务T1对资源R加上X锁,则只允许T1读取和修改R,其他事务可以读取R,但是不能修改R,除非事务T1解除了锁。
-
共享锁:又称为S锁或读锁。事务T2对资源R加上S锁,则允许读取R,其他事务也可以读取R。
2)按照资源划分
-
数据锁:当用户对表格中的数据进行insert、update和delete操作时将要用到数据锁,数据锁在表中获得并保护数据。
-
字典锁:当用户创建、修改和删除数据表时将要用到字典锁。字典锁用来防止两个用户同时修改同一个表结构。
(3)加锁的方法
1)行共享锁 RD(Row Share)
Lock table xx in row share mode;
2)行排他锁 RX(Row Exclusive)
Lock table xx in row exclusive mode;
3)共享锁 S(Share)
Lock table xx in share mode;
4)共享行排他锁 SRX(Share Row Exclusive)
Lock table xx in share row exclusive mode;
5)排他锁 E(Exclusive)
Lock table xx in exclusive mode;
7.3 死锁
当两个会话互相等待对方持有的资源而导致相互阻塞时,会出现死锁。
预防死锁的方法有:
1)执行事务时尽可能快地提交;
2)进行批量操作时,不同程序员操作表的顺序应该一致。