zoukankan      html  css  js  c++  java
  • Oracle专家高级编程 第三章 封锁和并行性

    锁定

    锁是一种机制,管理共享资源的并行访问,也就是concurrent问题

    当多个用户访问并更改数据或数据结构时,以适当的机制防止对相同的信息段进行修改

    在Oracle中

    • 事务处理是数据库的全部工作,
    • 只要必须,就应该推迟提交,而不是迅速提交,在必须提交时提交,而不是必须提交前提交
    • 只要需要,就应该保持对数据的锁定
    • Oracle行级锁不包含开销

    封锁问题

    丢失更新

    举例

    • 用户1检索(查询)一行数据
    • 用户2检索相同行
    • 用户1修改那个行,更新且未提交
    • 用户2修改那个行,更新且提交

    悲观封锁(悲观锁)

    在修改值之前就要发挥作用,即默认当前行数据一定会修改

    行被锁之后,另一个用户修改行的进程中, 会返回"ORA-00054 资源忙(Resource Busy)"的错误,该进程被阻塞,需要等待用户完成工作

    乐观封锁(乐观锁)

    select for update nowait
    • for update,排他锁(悲观锁)
    • nowait,通知Oracle该sql语句采用非阻塞的方式修改或删除数据,如果发现涉及到的数据被占有(被锁),则立即通知Oracle该资源被占用,返回错误信息

    使用悲观锁,当前正在修改的数据已经被check out了,没人能对其进行修改

    行锁不会对其他用户的读取造成限制

    阻塞

    当一个会话请求另一个会话保持的资源时,会发生阻塞,它会一直挂起

    4个DML语句会在数据库中阻塞

    • INSERT
    • UPDATE
    • DELETE
    • SELECT FOR UPDATE

    被阻塞的插入

    • 当用户拥有一个带有主键的表或其上有唯一约束时,两个会话同时试图用相同的值插入一行
    • 有一个会话会被阻塞,直到另一个会话提交或回滚
    • 前一种情况,被阻塞会话会收到关于值重复的错误;后一种情况,被阻塞会话紧接着执行

    阻塞的update和delete

    表示在代码中可能又了一个丢失更新的问题

    试图更新一个其他用户已经在更新的行(已经被锁定)

    通过select for update避免阻塞问题

    • 验证在将数据查询以后没有更改(丢失更新预防)
    • 锁定行(防止update或delete的阻塞)

    死锁

    Oracle死锁以后,会在服务器上创建一个跟踪文件,Oracle的死锁很少

    对父表修改之后,Oracle将在子表上放置完整的表锁定

    • 如果更新父表主键,子表没有索引会被锁定
    • 如果删除了父表的行,整个子表也将被锁定

    更新主键在RDMS中是一个巨大的"禁忌"

    什么情况下不需要索引外码

    • 不从父表删除
    • 不更新父表的唯一/主键值
    • 不从父表连接到子表

    锁定扩大

    当发生锁定扩大情况时,系统要降低锁定到粒度

    例如,数据库系统将一个表的100个行级锁定转换为单一的表级锁定,锁定扩大用于锁资源稀少的数据库中频繁使用

    Oracle不会扩大锁定,使用锁定转换,或锁定提升

    尽可能在最低一级使用锁定,如果使用 select for update子句,会创建两个锁定:

    • 一个锁定放在选择的行上
    • 另一个锁在ROW SHARE TABLE锁上,放置在表上

    所有针对表的其他语句是允许的,另一个会使用LOCK TABLE X IN SHARE MODE将表变为只读

    锁定类型

    DML锁定

    select,insert,update和delete,指定行锁定或者表锁定

    DDL锁定

    create,alter,DDL锁定保护对象结构的定义

    内部锁定和锁存器(latch)

    Oracle保护内部数据结构的锁定,分析一个查询并生成优化的查询方案时,它将"锁存"库高速缓存器,是轻量的低级船行化设备

    分布式锁定

    OPS使用的锁,用于保证不同的节点资源保持相互之间的一致,分布式锁由数据库实例所持有

    PCM(并行缓冲器管理,Parallel Cache Management)

    保护多个实例之间缓冲存储器中高速缓存数据块的锁

    DML锁定

    DML锁用于保证一行在一段时间内只有一个用户进行修改

    TX(事务)锁定

    事务初始化其第一次更改结果时获得,且一直保持,直到事务执行提交(COMMIT)或回滚(ROLLBACK)

    它用作一个排队机制,使其他会话等待该事务完成

    修改或者select for update的每一行指向一个相关的TX锁定

    Oracle锁定的处理方式

    • 找出要锁定行的地址
    • 到该行
    • 锁定行,除非使用nowait选项

    在Oracle锁定数据的行时,一个事务ID与包含数据的块相关

    • 当锁定被释放时,那个事务ID被留下,此ID对事务来说是唯一的
    • 代表回滚段的好、槽和序列号,放在保存行的块上,通知其他会话,我们占有这块资源
    • 其他会话看到这个ID,会去查询事务的活动状态,中间有一个排队机制,请求锁定的会话将排队等待事务完成

    物理属性参数

    • INITRANS -- 结构初始、预分配额大小,随遇索引,默认是2,对于表,默认是1
    • MAXTRANS -- 结构所能增长的最大尺寸,默认是255 

    默认情况下,每个块的生命从一个或两个事务槽开始,一个块可以拥有同时活动的事务数由MAXTRANS所限制

    也被块上空间可用性所限制,即受到逻辑和物理上的限制

    适当增加INITRNANS给预期的并行事务的数量在块上留出充足的空间

    TM(DML 入队)锁定

    保证更改表的内容时,表的结构不会被更改

    如果已经更新了一个表,将得到在那个表上的一个TM锁定,放置另一个用户在表上运行DROP或ALTER命令

    若试图在已经拥有TM锁定的表上执行DDL,将得到错误信息

    ORA-00054:resource busy and acquire with NOWAIT specified

    • 每一个事务中,只获得一个TX锁定,可以获得与所修改对象数量一样多的TM锁定
    • TM的ID1列是DML锁定对象ID

    系统中允许的TM锁定的总数是用户可配置的

    它可以设置为0,不意味着用户的数据库变成只读的数据库,而是不允许DDL

    使用alter table tablename,disable table lock命令批量删除TM锁

    DDL锁定

    DDL锁定在DDl操作期间自动针对对象放置,保护免于被其他会话更改

    例如使用alter table命令时,DDL锁在DDL期间保持,这是由隐式提交(或提交/回滚对)包含的DDL语句完成

    DDL总是提交,即使没有成功,可以使用自定义事务

    3种类型的DDL锁定

    独占的DDL锁定

    防止其他会话自己获得DDL锁定或TM锁定,意味着在DDL操作期间查询一个表,但不能修改

    共享DDL锁定

    保护饮用对象的结构,防止被其他会话修改,允许对数据的修改

    可打破的分析锁定

    允许对象在一些对象上注册信任,如果执行针对该对象的DDL,Oracle检查已经注册依赖关系的对象列表,使其无效,不能防止DDL发生

    • 关键字ONLINE修改实际建立索引,不使用DDL锁定,防止数据修改
    • Oracle只是在表上获得一个低级的TM锁定
    • 允许正常的DML操作

    持久化DDL语句期间所做的修改,完成后将应用更新到心的索引上,提高了数据可用性

    其他类型使用共享DDL锁定,

    锁定器和内部锁定(入队)

    锁存器和排队时轻量的序列化设备,用于协调多用户对共享的数据结构、对象和文件的访问

    锁存器

    锁存器时在极短时间内,例如修改内存中数据结构时间内保持的锁定,来保护内存结构

    没有排队额锁存器的等待者,只有不断重试

    Oracle使用原子指令进行锁存器操作,设置和释放指令是原子化的,运行效率高

    万一一个锁存器持有者不正常的死掉,可以清除,由PMON执行

    入队

    一个更加复杂的串行设备,与锁存器的区别在于允许请求者排队等待资源

    • 使用锁存器,请求者立刻被告知是否占有锁存器
    • 使用入队,请求者被阻塞,直到实际获得为止

    入队可以在不同等级获得,可以拥有许多"共享"锁定,用不同"共享能力"的锁

    手动封锁和用户自定义锁定

    手动封锁

    select for update,最常用

    lock table 比较粗糙,很少使用,锁表不锁行,如果写入大批更新,影响表中大部分行,可以使用

    lock table in exclusive mode

    可以保证不被用户阻塞

    创建自己的锁定

    借助DBMS_LOCK包

    可以用这个包串行化对Oracle外部资源的访问

    例如一个消息例程,文件在外部,Oracle不能协调试图同时修改它的用户,可以引入DBMS_LOCK包

    对一个文件打开、写入或关闭操作前,文件进入独占模式前,请求一个指定的锁定,关闭文件后,手动释放锁定(有点像ReentrantLock)

    并行控制

    数据库提供的函数集,允许许多用户同时访问和修改数据,锁定的实现大概可以决定应用程序的并行程度

    并行控制胜过锁定,多版本,写入不阻塞读取,

    事务隔离级别

    脏读

    允许读一个没有提交的,或"脏"数据

    非重复读

    读者在时间T1读取一行,试图的时间T2再次读取行,该行可能已经更改或者消失

    幻象读

    如果时间T1执行了一个查询,并在时间T2再次执行,附加的行可能已经添加到数据库,影响结果

    Oracle还支持只读模式

    Read Uncommitted 隔离等级

    允许脏读,Oracle不利用脏读,其目标在于提供"迎合"非阻塞读取的标准

    Read Committed 隔离等级(最常用的隔离级别)

    一个事务只能读取在事务开始前提交的数据,没有脏读

    支持非阻塞读取

    Repeatable Read 隔离等级

    获得一致性答案,给定查询的结果必须与有关的时间点是一致

    • 在一个使用共享锁的数据库中,数据读取器将阻塞数据写入器,Oracle选择多版本的模型提供读一致性答案,降低并行程度

    如果产生死锁,之后事务中的一个将成为牺牲者,被杀死

    • 共享读取锁,数据的读取器和写入器经常互相死锁

    Oracle使用多版本,拥有语句级的读取一致性,读取不阻塞写入,没有死锁

    预防丢失更新

    Repeatable Read 的普通用法就是为了预防丢失更新

    Oracle中,需要Repeatable Read,不是用 select for update nowait,而是将隔离等级设置为Serializable

    Serializable事务,使得我们从语句级获得的读取一致性扩展到事务,事务中执行的每一个查询答案固定在事务开始的时间点

    Serializable 隔离等级

    最具限制性的事务隔离等级,事务之间互不可见,隔离代价

    ORA-08177:can't serialize access for this transaction

    不论何时试图更新从事务开始以后已经更改的行,都会获得这个消息,什么时候适合用该隔离级别

    • 只有很高的没有修改相同数据的可能性
    • 与奥事务的读强一致性
    • 进行短的事务

    此方法的可伸缩行,足够运行全部的TPC-C(OLTP标准测试)

    只读事务

    类似于Serializable事务,区别在于不允许修改,不容易出现ORA-8177错误,该事务支持报告的需要

    该事务可能会出现

    ORA-1555 snapshot too old 错误,当其他人活跃的修改正在读取的数据,可能报此错误

    论读书
    睁开眼,书在面前
    闭上眼,书在心里
  • 相关阅读:
    C#单例模式的三种写法
    【TFS】增加组员,以及用户权限分配
    Mongodb实用网址记录
    关于JS 对象与JSON对象
    多线程操作
    检测到有潜在危险的 Request.Form 值
    JavaScript 【正则表达式验证数字代码】
    【SQL】大杂烩
    IE 中创建 子窗口 传值 与接收值 【window.showModalDialog】
    UITabbarView Tabbar
  • 原文地址:https://www.cnblogs.com/YC-L/p/14657561.html
Copyright © 2011-2022 走看看