为啥使用 READ UNCOMMITTED 隔离级别?
Posted
技术标签:
【中文标题】为啥使用 READ UNCOMMITTED 隔离级别?【英文标题】:Why use a READ UNCOMMITTED isolation level?为什么使用 READ UNCOMMITTED 隔离级别? 【发布时间】:2010-03-18 15:30:06 【问题描述】:通俗的说,使用的优缺点是什么
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
在查询 .NET 应用程序和报告服务应用程序?
【问题讨论】:
【参考方案1】:此隔离级别允许脏读。一个事务可能会看到其他事务所做的未提交更改。
为了保持***别的隔离,DBMS 通常会在数据上获取锁,这可能会导致并发丢失和高锁定开销。这个隔离级别放宽了这个属性。
您可能需要查看Wikipedia article on READ UNCOMMITTED
以获取一些示例和进一步阅读。
您可能也有兴趣查看 Jeff Atwood 的 blog article,了解他和他的团队如何在 Stack Overflow 早期解决死锁问题。根据杰夫的说法:
但是
nolock
危险吗?你能结束吗? 使用read uncommitted
读取无效数据?是的,理论上。你会 发现不缺数据库 开始的建筑宇航员 将酸科学放在你和所有人身上 但拉建筑物火警时 你告诉他们你想试试nolock
。 这是真的:这个理论很可怕。但 这就是我的想法:“理论上有 理论和理论没有区别 实践。在实践中是有的。”我永远不会推荐使用
nolock
作为一般的“对你有什么好处” 任何数据库的蛇油修复 您可能遇到的死锁问题。你 应尝试诊断源头 先说问题。但实际上,将
nolock
添加到您绝对知道是简单、直接的只读事务的查询中似乎永远不会导致问题... 只要您知道自己在做什么。 >
您可能要考虑的READ UNCOMMITTED
级别的替代方案是READ COMMITTED SNAPSHOT
。再次引用 Jeff:
快照依赖于一种全新的数据更改跟踪方法……不仅仅是轻微的逻辑更改,它还需要服务器以不同的物理方式处理数据。一旦启用了这种新的数据更改跟踪方法,它就会创建每个数据更改的副本或快照。 通过在争用时读取这些快照而不是实时数据,读取时不再需要共享锁,并且整体数据库性能可能会提高。
【讨论】:
作者似乎暗示 read uncommitted / no lock 将返回最后提交的任何数据。我的理解是未提交的读取将返回最后设置的任何值,即使是未提交的事务也是如此。如果是这样,结果将不会是检索“过时几秒钟”的数据。它会(或者至少如果写入您读取的数据的事务被回滚)正在检索不存在或从未提交的数据。我弄错了吗? 很好的答案。顺便说一句,据我所知,Oracle 默认具有“快照”,可能比 Sql Server 引入它早了几十年。当我开始使用 SQL Server 时,我感到非常失望,我发现所有并发问题都可以使用“原始”锁定机制解决。从未在 Oracle 中看到“读取未提交”。练习者和宇航员一样快乐。READ UNCOMMITTED
can also cause you to read rows twice, or miss entire rows。如果您在阅读时发生页面拆分,那么您可能会丢失整个数据块。 WITH(NOLOCK)
仅应在结果的准确性不重要时使用
@DanielNolan,推荐这篇文章很危险,因为Jeff didn't know 什么he was doing。 Read-uncommited 只对读取数据有意义,永远不会被修改。尝试使用它来读取将要写入的表意味着您实际上会读取会回滚的内容。不仅您正在读取几秒钟前的数据,而且您...................... ...................................................... ....
....................... 正在读取 的数据从来没有甚至被承诺。这就是损坏读取的定义。如果您要根据这些未提交读取的结果进行写入,那么您实际上将写入损坏的数据。文章还指出,“在 Web 应用程序上成长起来的 mysql 比 SQL Server 开箱即用的悲观程度要低得多”。不正确,Sql Server 默认工作在 read-commited 级别,而 MySQL 默认工作在可重复读,five levels 远离 read-uncommitted。【参考方案2】:
read uncommited
我最喜欢的用例是调试事务中发生的事情。
在调试器下启动您的软件,当您单步执行代码行时,它会打开一个事务并修改您的数据库。当代码停止时,您可以打开查询分析器,设置读取未提交的隔离级别并进行查询以查看发生了什么。
您还可以使用它来查看长时间运行的过程是否卡住或使用带有count(*)
的查询正确更新您的数据库。
如果您的公司喜欢制作过于复杂的存储过程,那就太好了。
【讨论】:
【参考方案3】:这对于查看长插入查询的进度、进行粗略估计(如COUNT(*)
或粗略的SUM(*)
)等很有用。
换句话说,脏读查询返回的结果很好,只要您将它们视为估计值并且不根据它们做出任何关键决策。
【讨论】:
【参考方案4】:优点是在某些情况下它可以更快。缺点是结果可能是错误的(尚未提交的数据可能会被返回)并且不能保证结果是可重复的。
如果您关心准确性,请不要使用它。
更多信息在MSDN:
实现脏读或隔离级别 0 锁定,这意味着不会发出共享锁,也不会使用排他锁。设置此选项时,可以读取未提交或脏数据;数据中的值可以更改,并且行可以在事务结束之前在数据集中出现或消失。此选项与在事务中的所有 SELECT 语句中的所有表上设置 NOLOCK 具有相同的效果。这是四个隔离级别中限制最少的。
【讨论】:
这会如何影响查询速度? @Kip -select
语句不必等待获取被其他事务独占锁定的资源的共享锁。【参考方案5】:
什么时候可以使用READ UNCOMMITTED
?
经验法则
良好:大汇总报告显示不断变化的总数。
有风险:几乎所有其他事情。
好消息是大多数只读报告都属于良好类别。
更多细节...
可以使用:
几乎所有面向用户的当前非静态数据汇总报告,例如年初至今的销售额。 它存在误差范围(可能这可能涵盖了商业智能部门将在 s-s-rS 等中所做的大部分工作。当然,前面有 $ 符号的东西除外。许多人对金钱的计算比应用于服务客户和产生金钱所需的相关核心指标要热情得多。 (我责怪会计师)。
有风险时
任何详细级别的报告。如果需要该详细信息,则通常意味着每一行都与决策相关。事实上,如果您无法在不阻塞的情况下提取一个小子集,则可能是因为它当前正在被编辑。
历史数据。它很少产生实际的差异,但尽管用户明白不断变化的数据不可能完美,但他们对静态数据的感觉却不尽相同。脏读在这里不会受到伤害,但偶尔会出现双重阅读。既然您不应该对静态数据设置块,为什么要冒险呢?
几乎所有支持应用程序的东西都具有写入功能。
即使是好的场景也不行。
是否有任何应用程序或更新进程使用大单笔交易?删除然后重新插入您要报告的大量记录的那些?在那种情况下,你真的不能在这些表上使用NOLOCK
来做任何事情。
【讨论】:
关于报告的要点。实际上,我想到的第一个想法是,当用户看到一些数据准确性不那么重要的 UI 网格时,是否应该将read uncommitted
用于 Web 应用程序。用户只想快速了解可能存在哪些记录,并且可能需要一些分页、排序和过滤。只有当用户单击编辑按钮时,我才会尝试读取具有更严格隔离级别的最新记录。这种方法在性能方面不应该更好吗?
是的,我认为这是合理的。请记住,更重要的问题是确保数据在点击编辑按钮和提交时间之间没有被其他人更改。您可以通过启动获取数据的事务来处理这个问题,例如 select item from things with (UPDLOCK)
。在那里设置一个快速超时,这样如果它不能快速获取锁,它就会告诉用户它正在被编辑。这不仅可以保护您免受用户的侵害,还可以保护开发人员。这里唯一的麻烦是您必须开始考虑超时以及如何在 UI 中处理它。【参考方案6】:
关于报告,我们在所有报告查询中使用它,以防止查询陷入数据库。我们可以这样做,因为我们提取的是历史数据,而不是微秒级数据。
【讨论】:
【参考方案7】:在源极不可能更改的情况下使用 READ_UNCOMMITTED。
读取历史数据时。例如,两天前发生的一些部署日志。 再次读取元数据时。例如基于元数据的应用程序。如果您知道在 fetch 操作期间源可能会发生变化,请不要使用 READ_UNCOMMITTED。
【讨论】:
我觉得情况正好相反。首先,静态数据应该可以很好地读取,没有块。如果它确实阻塞,那么您现在发现有一个重要的挂起事务问题需要解决。此外,无论他们为去年的年度报告打印什么,用户都希望这能匹配到小数点后的最后一位。他们通常不期望他们知道不断变化的报告相同。这不适用于详细的、对时间敏感的或财务报告,但如果 1000 中的 1 个输入错误是可以容忍的,那么READ UNCOMMITTED
也是如此。
TLDR:如果数据不会更改,则不需要 READ UNCOMMITTED,因为无论如何都没有块。如果您错了并且它确实发生了变化,那么您阻止用户获取比预期更脏的数据是一件好事。
是的,我倾向于同意@Adamantish 的观点 - 大多数情况下,您可以从READ UNCOMMITTED
中受益,当您的数据被积极使用并且您希望减少服务器上的负载以避免可能的死锁时和事务回滚只是因为一些用户不小心滥用了带有数据网格的网页中的“刷新”按钮。同时查看一堆记录的用户,通常不会太在意数据是否有点过时或部分更新。只有当用户即将编辑记录时,您才可能希望为他/她提供最准确的数据。【参考方案8】:
这将为您提供脏读,并向您显示尚未提交的事务。这是最明显的答案。我认为仅仅为了加快阅读速度而使用它并不是一个好主意。如果您使用良好的数据库设计,还有其他方法可以做到这一点。
注意没有发生的事情也很有趣。 READ UNCOMMITTED 不仅忽略其他表锁。它本身也不会导致任何锁定。
假设您正在生成一个大型报告,或者您正在使用大型且可能很复杂的 SELECT 语句将数据迁移出数据库。这将导致在事务期间可能升级为共享表锁的共享锁。其他事务可以从表中读取,但无法更新。如果它是一个生产数据库,这可能不是一个好主意,因为生产可能会完全停止。
如果您使用 READ UNCOMMITTED,您将不会在表上设置共享锁。您可能会从一些新事务中获得结果,或者您可能不依赖于数据插入表的位置以及您的 SELECT 事务读取了多长时间。例如,如果发生页面拆分(数据将被复制到数据文件中的另一个位置),您也可能会获得两次相同的数据。
因此,如果在执行 SELECT 时可以插入数据对您非常重要,那么 READ UNCOMMITTED 可能是有意义的。您必须考虑到您的报告可能包含一些错误,但如果它基于数百万行并且在选择结果时只有少数行被更新,这可能“足够好”。您的事务也可能一起失败,因为可能无法保证行的唯一性。
更好的方法可能是使用 SNAPSHOT ISOLATION LEVEL,但您的应用程序可能需要进行一些调整才能使用它。例如,如果您的应用程序对一行进行了排他锁以防止其他人读取它并进入 UI 中的编辑模式。 SNAPSHOT ISOLATION LEVEL 也会带来相当大的性能损失(尤其是在磁盘上)。但是你可以通过在问题上投入硬件来克服这个问题。 :)
您还可以考虑恢复数据库的备份以用于报告数据或将数据加载到数据仓库中。
【讨论】:
【参考方案9】:它可以用于一个简单的表,例如在一个只插入的审计表中,其中没有对现有行的更新,也没有对其他表的 fk。该插入是一个简单的插入,它没有或很少有回滚的机会。
【讨论】:
【参考方案10】:我现在总是使用 READ UNCOMMITTED。它的速度很快,问题最少。在使用其他隔离时,您几乎总会遇到一些阻塞问题。
只要您使用 Auto Increment 字段并多注意插入,然后就可以了,您就可以告别阻塞问题。
您可以使用 READ UNCOMMITED 出错,但老实说,很容易确保您的插入是完整的证明。使用来自选择的结果的插入/更新只是您需要注意的事情。 (在此处使用 READ COMMITTED,或确保脏读不会导致问题)
所以去脏读(特别是大型报告),你的软件会运行得更流畅...
【讨论】:
这是非常不准确的,只是触及了 nolock 可能发生的问题的表面。页面可以拆分,连接可以失败,您可以读取不存在的数据或重复数据。没有办法让它的使用万无一失:你不能相信在这个隔离级别下任何东西的准确性。 READ COMMITTED SNAPSHOT 是一种不那么危险的“假灵丹妙药” @MarkSowul 这个答案的否决似乎对我不公平。 @Clive 很清楚他会切换到Committed
进行插入和更新。至于其他问题,他在提到使用自动递增键时也表现出对分页问题的认识。我同意他的观点,几乎所有只供人类阅读的现场报道都可以容忍小数点后的细微差异。我同意对于注定要被机器读取和转换的详细列表或数据是另一回事,Clive 也是如此。
此评论还表明对 nolock 可能出现的问题缺乏全面了解。 “最后一位小数的细微差异”几乎没有涵盖它。即使触及“仅使用已提交的读取来插入/更新”作为一般建议也是错误的(如果它是“如果它不存在则插入记录”怎么办?)。无论如何,“[read uncommitted] 速度很快,问题最少”是绝对错误的。
郑重声明,我同意您的回答,Adamantish:大致准确的聚合,仅此而已。以上是关于为啥使用 READ UNCOMMITTED 隔离级别?的主要内容,如果未能解决你的问题,请参考以下文章
解决“set transaction isolation level read uncommitted;“命令无法修改MySQL数据库隔离级别的问题
MySQL数据库事务各隔离级别加锁情况--read uncommitted篇(转)
数据库事务特征数据库隔离级别,以及各级别数据库加锁情况(含实操)--read uncommitted篇
mysql 5.7.16 然后设置 事务隔离级别为 read uncommitted 不起作用.以下是截图.请大神帮帮我.