SqlServer性能優化 提高並發性能二(九)


 

    補充上一篇修改用非聚集索引:

    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. 索引對隔離級別的影響、阻塞的監視

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM