SQL Server 幻读 的真实案例

数据库中有表[01_SubjectiveScoreInfo],要实现表中的数据只被查出一次,此表数据量较大,有三四百万数据。表结构也确实不是很合理,无法修改表结构,即使是新增一个字段也会有相当大的修改量。

因之前代码中存在大量的insert into select *的语句,加一个字段什么也不做也会导致整个项目瘫痪,当然我不想去讨论前人的代码质量。

于是乎我加了一个新表[01_SubjectiveScoreInfoFlag]来进行记录取过的记录ID。于是就有了如下的代码:

BEGIN TRAN

                    SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

                    INSERT INTO [01_SubjectiveScoreInfoFlag](ID)
                    SELECT TOP 100 SS.ID
                    FROM [01_SubjectiveScoreInfo] AS SSINNER JOIN SubjectiveItemInfo AS SI                                                 ON SS.TestCode=SI.TestCode
                          AND SS.MajorQuestionID=SI.MajorQuestionID
                          AND SS.MinorQuestionID=SI.MinorQuestionID
                    WHERE SS.TestCode=‘‘‘ + @TestCode + ‘‘‘
                      AND SI.QuestionGroupCode=‘‘‘ + @QuestionGroupCode + ‘‘‘
                      AND (SI.MinorQuestionCount=0
                        OR SI.MinorQuestionID>0)
                      AND NOT EXISTS
                      (
                        SELECT TOP 1 1
                        FROM [01_SubjectiveScoreInfoFlag]
                        WHERE ID = SS.ID
                      )

                COMMIT TRAN

此处用到了事务,并且指定了隔离级别。这个是必须的,我们之前就是没有指定,单线程无论你怎么调用这段代码运行都非常可靠,但多线程模拟并发调用时就出现了非常严重的重复。

上面的代码也并没有解决问题,原因是锁用得不对,多线程直接就出现了死锁现象。因之前对于隔离级别没什么经验,我一度曾经以为这个问题是无解的,直到今天突然被解开了。

按照我自己的理解,是因为锁的级别不够,导致了资源争抢。

就相当于上厕所时,一定要获取完全的排它锁,关好门不让其它人进来;否则如果其它人进来了,虽然他没有占到位子,但他拿走了手纸。你占着位子却没有手纸,他拿着手纸却没有位子,双方互不相让,谁也无法完成上厕所的事务,相持不下,进而导致死锁。这时就需要厕所管理出面,要么强制让你让出位子走人,不管理你擦没擦屁股;要么抢来手纸赶走他,任他拉到裤子上。

于是乎就有了下面的代码:

BEGIN TRAN

                    SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

                    INSERT INTO [‘[email protected]+‘_SubjectiveScoreInfoFlag](ID)
                    SELECT TOP 100 SS.ID
                    FROM [‘[email protected]+‘_SubjectiveScoreInfo] AS SS WITH(UPDLOCK)
                      INNER JOIN SubjectiveItemInfo AS SI WITH(UPDLOCK)
                        ON SS.TestCode=SI.TestCode
                          AND SS.MajorQuestionID=SI.MajorQuestionID
                          AND SS.MinorQuestionID=SI.MinorQuestionID
                    WHERE SS.TestCode=‘‘‘ + @TestCode + ‘‘‘
                      AND SI.QuestionGroupCode=‘‘‘ + @QuestionGroupCode + ‘‘‘
                      AND (SI.MinorQuestionCount=0
                        OR SI.MinorQuestionID>0)
                      AND NOT EXISTS
                      (
                        SELECT TOP 1 1
                        FROM [‘[email protected]+‘_SubjectiveScoreInfoFlag] WITH(UPDLOCK)
                        WHERE ID = SS.ID
                      )

                COMMIT TRAN

UPLOCK是排它锁,这样就没有死锁了。

时间: 2024-08-09 01:09:33

SQL Server 幻读 的真实案例的相关文章

sql server服务启动不了(案例)

sql server服务启动不了 1)在启动 SQL Server(SQLEXPRESS)服务时提示如下错误:在本地计算机无法启动sql server(sqlexpress)服务错误3:系统找不到指定的路径那位遇到过这样的问题,我在网上找了些相关的资料可是无法解决 解决方法: 1.先去服务里找到 SQL Server(SQLEXPRESS),看它的属性,把可执行文件的路径复制下来 2.到注册表找到HKEY_LOCAL_MACHINE/SYSTEM/CONTROLSET001/Services/M

SQL Server逻辑读-预读-物理读

SQL Server逻辑读-预读-物理读    SQL Server 存储数据的方式        1.页是最小的操作单元,也就是说从磁盘读取数据库的时候最少读取一页,每一页的大小是8KB,SQL SERVER对于页的读取是原子性,要么读完一页,要么完全不读,不会有中间状态 2.区是8个连续的页组成的,区是最小的分配单元,当需要空间时最少分配一个区的空间. 看图说话,两个表的结构完全一样,一个插入四条数据,另一个插入100条数据,结果大小都为0.008: SQL SERVER一页的总大小为:8K

SQL Server数据库邮件发送异常案例

最近遇到两起关于SQL Server数据库邮件发送异常的案例,这些问题也有点意思,顺便记录一下.方便以后遇到类似问题的人为这些问题抓狂!多提供一点思路. 案例1:我们一台数据库服务器突然发送邮件都不行了,出现问题时,检查邮件发送记录,你会发现发送状态都是failed. SELECT * FROM msdb.dbo.sysmail_faileditems   SELECT * FROM msdb.dbo.sysmail_mailitems 检查Database Mail Log,你会发现有下面一些

初谈SQL Server逻辑读、物理读、预读

前言 本文涉及的内容均不是原创,是记录自己在学习IO.执行计划的过程中学习其他大牛的博客和心得并记录下来,之所以想写下来是为了记录自己在追溯的过程遇到的几个问题,并把这些问题弄清楚. 本章最后已贴出原文地址. 1.SQL Server的数据存储方式 要理解逻辑读.物理读.预读这三个概念,先要搞懂SQL Server的数据存储方式. SQL Server数据库包括数据文件和日志文件,一个数据库可以有一个或多少数据文件.日志文件.所有的数据存储在数据文件中,数据文件可以划分为再小的单元,我们称为“页

SQL Server逻辑读、预读和物理读

SQL Server数据存储的形式 预读:用估计信息,去硬盘读取数据到缓存.预读100次,也就是估计将要从硬盘中读取了100页数据到缓存. 物理读:查询计划生成好以后,如果缓存缺少所需要的数据,让缓存再次去读硬盘.物理读10页,从硬盘中读取10页数据到缓存. 逻辑读:从缓存中取出所有数据.逻辑读100次,也就是从缓存里取到100页数据. SQL Server存储的最小单位是页,每一页大小为8K,SQL Server对于页的读取是原子性的,要么读完一页,要么完全不读.即使是仅仅要获得一条数据,也要

浅谈SQL Server逻辑读、物理读和预读

[摘要] 对SQL Server数据库进行性能优化时,有一个重要的工作是优化IO开销.通过开启Statistics IO,我们可以了解到语句执行的IO开销,包含物理读.逻辑读.预读等.而我们是否了解这三者的区别和联系呢,相信很多人都不是很理解,下面我们一起来探索这三者究竟分别代表着什么. [正文] 一  .SQLServer数据存储方式 SQL Server的数据库包括数据文件和日志文件,一个数据库可以有一个或多个数据文件或日志文件. 所有的数据都存储在数据文件中,而数据文件可以划分为再小的单元

应对黑客攻击SQL SERVER数据库中的一个案例

最近发现挂在网上server不知怎的,重新启动,那server现在主要是开始IIS服务,SQL SERVER 服务. 远程登录.发现系统响应十分缓慢.一个明显的停滞感,打开任务管理器,CPU在基本用法30%大约.打开事件查看器,大量的级别为信息来源为MSSQL$PNCSMS,事件ID为18456.任务类别为登录的记录.差点儿24小时不间断,每秒钟有15次个记录,每一个记录的内容大体同样,如"用户 'sa' 登录失败. 原因: 找不到与所提供的名称相匹配的登录名. [client: 60.191.

sql server 中的简单查询案例

/** 名称:人力资源管理系统数据库*/ /*切换到系统数据库master:*/USE master;GO /*若当前系统中存在HR表,则删除之:*/IF EXISTS (SELECT 1 FROM sysdatabases WHERE name = 'HR')BEGIN DROP DATABASE HR;ENDGO --创建数据库HRcreate database HRgo/*切换到HR数据库*/USE HR;GO----------------------------------------

Performance Monitor4:监控SQL Server的IO性能

SQL Server的IO性能受到物理Disk的IO延迟和SQL Server内部执行的IO操作的影响.在监控Disk性能时,最主要的度量值(metric)是IO延迟,IO延迟是指从Application创建IO请求,到Disk完成IO请求的时间延迟.如果物理Disk不能及时完成IO请求,跟不上请求负载的速度,那么SQL Server就容易出现性能问题.SQL Server内部在执行一些特定的操作时,会和Disk做读写交互,这也会影响物理硬盘响应SQL Server的IO请求的性能,使查询进程处