補充上一篇修改用非聚集索引:
update Employee set age=age+1 from Employee with(index=nc_Employee_Age) where age<30
執行計划:

並發訪問控制隔離級別:
1.讀提交:默認行為,讀取時請求S鎖
set transaction isolation level read committed select * from Employee where age=34
2. 臟讀:讀取時不請求S鎖,不會受到其他X鎖限制
set transaction isolation level read uncommitted select * from Employee
3. 已提交快照讀:更新時將老的數據復制到 Tempdb:read_committed_snapshot
alter database HRDB set read_committed_snapshot on
設置成單用戶模式:

設置事物:
begin tran update Employee set age=age+1 where age>=30
執行語句:
select * from Employee

這條語句也可以執行:
select * from Employee where age>30
這條可以執行:
update Employee set age=age+1 from Employee with(index=nc_Employee_Age) where age<30
沒有復制的還是從索引或基表中讀取。所以可以讀取出數據
這條不能執行:
update Employee set age=age+1 from Employee with(index=nc_Employee_Age) where age>30
打開數據庫的讀提交快照。 對記錄進行操作時,會把排他鎖的數據放到Tempdb數據庫中,訪問的時候直接讀出Tempdb的數據。
4.可重復讀:事物結束前,不釋放獲取的S鎖,可能會形成死鎖
create table Products(id int identity(1,1),name varchar(500),UnitPrice money)
delete from Products where id=2
insert Products values ('p1',13)
insert Products values('p2',5)
--業務邏輯:單價大於10的優惠10
begin tran
declare @UnitPrice money
set @UnitPrice=(select @UnitPrice from Products where id=1)
--執行等待的時間
waitfor delay '00:00:20'
if @UnitPrice>10
update Products set UnitPrice=UnitPrice-10 where id=1
commit tran
第二個人執行打六折的業務:
update Products set UnitPrice=UnitPrice*0.6
結果:

事務中盡量不要放查詢語句:13*0.6=7.8 7.8-10=-2.2
實在要查詢語句,如何解決呢?
刪除上述表:drop table Products 重新創建
1. --在開啟事務之前 設置事務的級別 可重復讀
--在開啟事務之前 設置事務的級別 可重復讀 set transaction isolation level repeatable read --業務邏輯:單價大於10的優惠10 begin tran declare @UnitPrice money set @UnitPrice=(select @UnitPrice from Products where id=1) --執行等待的時間 waitfor delay '00:00:20' if @UnitPrice>10 update Products set UnitPrice=UnitPrice-10 where id=1 commit tran
2.打六折:
update Products set UnitPrice=UnitPrice*0.6
結果:

with(updlock) 可重復讀的方式 可以保護線程。代碼如下:
begin tran declare @UnitPrice money set @UnitPrice=(select @UnitPrice from Products with(updlock) where id=1) --執行等待的時間 waitfor delay '00:00:20' if @UnitPrice>10 update Products set UnitPrice=UnitPrice-10 where id=1 commit tran update Products set UnitPrice=UnitPrice*0.6

5. 串行化:訪問的行和按順序下一行放置范圍鎖,防止不必要操作與插入數據
業務背景:給分組為:‘group1’的員工發獎金,加入了新的員工
create table Employees(id int identity(1,1),name varchar(500),groups varchar(500),salary money)
insert Employees values('caojian','grouup1',3000)
insert Employees values('ligang','grouup1',1000)
insert Employees values('huang','grouup2',1500)
insert Employees values('sunliyuan','grouup2',2000)
業務邏輯的事務語句:
begin tran declare @count int set @count=(select COUNT(*) from Employees where groups='grouup1') declare @avgsalary money set @avgsalary=20000/@count waitfor delay '00:00:20' update Employees set salary=salary+@avgsalary where groups='grouup1' commit tran
第二個線程執行的語句:
insert Employees values ('newemployee','grouup1',0)
這種結果是不對的:

--設置串行化
set transaction isolation level serializable begin tran declare @count int set @count=(select COUNT(*) from Employees where groups='grouup1') declare @avgsalary money set @avgsalary=20000/@count waitfor delay '00:00:20' update Employees set salary=salary+@avgsalary where groups='grouup1' commit tran
進行添加:
insert Employees values ('newemployee','grouup1',0)
執行查詢語句:

針對group創建索引:
--針對group創建聚集索引 create clustered index c_Employees_group on Employees (groups)
執行事務:
--設置串行化 set transaction isolation level serializable begin tran declare @count int set @count=(select COUNT(*) from Employees where groups='grouup1') declare @avgsalary money set @avgsalary=20000/@count waitfor delay '00:00:20' update Employees set salary=salary+@avgsalary where groups='grouup1' commit tran
執行以下三條語句:
insert Employees values ('newemployee','grouup1',0)
insert Employees values ('newemployee','grouup2',0)
insert Employees values ('newemployee','grouup3',0)
grouup1有影響,grouup2和grouup3無影響。

6. 快照:比已提交快照讀取更嚴格,試圖對修改數據應用X(排他鎖),如果已發生改變,事物失敗 allow_snapshot_isolation
創建表:
create table SnapShotTB(id int identity(1,1),name varchar(500),age int)
insert SnapShotTB values('caojian',33)
給數據庫進行配置:
--打開配置數據庫的一個選項 alter database HRDB --允許快照隔離 set allow_snapshot_isolation on
設置事物的隔離級別:
--設置事物的隔離級別為快照 set transaction isolation level snapshot begin transaction declare @age int set @age=(select age from SnapShotTB where name='caojian') waitfor delay '00:00:20' update SnapShotTB set age =age+1 where name='caojian' commit tran
第二個線程:
update SnapShotTB set age =age-1 where name='caojian'
報的錯誤:
減少阻塞與死鎖的建議:
1.合適的索引
2.合適的分區
3.調整合適的隔離級別
4.查詢條件的有限性
5.相同的順序操作資源
6.短的事務
.NET 調用的案例:(EF CodeFirst)
1.連上數據庫。
2.引入命名空間。

3.在領域層引入命名空間:
using System.Transactions;
using System.Data;
using System.Linq;
4.代碼:
/// <summary>
/// 獎金處理的業務
/// </summary>
public void ProcessSalary()
{
TransactionOptions option = new TransactionOptions();
//指定的隔離級別(串行化)
option.IsolationLevel = System.Transactions.IsolationLevel.Serializable;
using (TransactionScope scope=new TransactionScope(TransactionScopeOption.Required,option))
{
//連到數據訪問的上下文
HRUser dbcontext = new HRUser();
var employees = dbcontext.Set<Employees>().Where(p => p.groups == "grouup1").ToList();
//取得groupp1組的人數
int count = employees.Count;
//把獎金進行employees平分
decimal salary = 20000 / count;
//對每個人的值進行跟新
foreach (var emoloyee in employees)
{
dbcontext.Set<Employees>().Attach(emoloyee);
//狀態是可修改的
dbcontext.Entry<Employees>(emoloyee).State = System.Data.Entity.EntityState.Modified;
emoloyee.salary = emoloyee.salary + salary;
}
dbcontext.SaveChanges();
//事物的完成
scope.Complete();
}
}
調用:
protected void Button1_Click(object sender, EventArgs e)
{
Employees es = new Employees();
es.ProcessSalary();
}
點擊button數據庫更新成功。
7. 索引對隔離級別的影響、阻塞的監視
