数据库
首页 > 数据库> > 数据库性能优化4

数据库性能优化4

作者:互联网

概述

  隔离级别

  锁

  事务

  总结

  In-Memoery Database(内存数据库)

  分库分表

1,锁和事务

  隔离级别:是事务实现的前提,隔离级别影响锁的行为,从而影响事务的并发性;

    ),隔离级别只对当前链接有效;在会话中,是可以修改隔离级别的

    ),隔离级别分成四个等级;由低到高分别为Read uncommitted 、Read committed 、Repeatable read 、Serializable

    1),Read uncommitted(读未提交):一个事务可以读取其他未提交事务的数据;

      它的优势是执行效率最快,避免大量阻塞,它的劣势会产生脏读问题

      适用的场景:在一些不重要的查询显示中使用它可以提高并发,比如未读信息;待办信息记录等

      示例:事务A读取a字段,事务B修改a字段;事务B已修改a字段的数据,

        但是事务B未提交(事务B随时有可能会回滚的可能性);事务A读取了事务B修改的a字段数据;

        如果事务B正常提交,事务A读取到的数据也不是预期的;如果事务B回滚,那么事务A读取的数据就是不存在的;

    脏读:事务A读到了事务B还没有提交的数据

      示例事务A先启动,事务A设置隔离级别为Read uncommitted;第一次读取[Description]字段后,等待4秒;再去读[Description]字段

        事务B后启动,在事务A结束之前启动;负责修改记录的字段值,延迟9秒,最后选择回滚;并查询回滚后的记录值

------脏读事务A,先启动它
begin transaction
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
select [Description] from  Company4 where Id='C13192'
waitfor delay '00:00:04:000'
select [Description] from  Company4 where Id='C13192'
commit

  

---脏读事务B,在事务A启动之后,在事务A结束之前启动
begin transaction
update Company4 set [Description]='贝蒂哈使'
where Id='C13192'
waitfor delay '00:00:09:000'
rollback
select [Description] from Company4 where Id='C13192'

  

    2),Read Committed(读提交):读已提交,一个事务要等另一个事务提交后才能读取数据;

      它的优势是可以解决脏读问题,它的劣势是仍然会产生重复读和幻读的问题

      示例:事务B在对a进行更新(UPDATE)操作时,事务A要等待事务B这个更新操作事务提交后才能读取a

    重复读:在一个事务里面读取了两次某个数据,读出来的数据不一致;不可重复读对应的是修改,即UPDATE操作      

      示例:company4表,在sql server中开启两个事务,事务A先启动,事务A会在第一个读取后等待9秒;事务B后启动;事务B等待4秒,第二次事务A读到的同数据和第一次读到同一的数据已经发生了变化;这是一个【重复读】  

------重复读演示事务A,先启动它
begin transaction
select [Description] from  Company4 where Id='C13192'
waitfor delay '00:00:09:000'
select [Description] from  Company4 where Id='C13192'
commit

 

------重复读事务B,在事务A启动后再启动它 ,但要在事务A等待9秒之前启动它
begin transaction
update Company4 set [Description]='你好'
where Id='C13192'
waitfor delay '00:00:04:000'
commit

  

    3),Repeatable read(可重复读):在数据读出来之后加锁,类似"select * from XXX for update",明确数据读取出来就是为了更新用的,所以要加一把锁,防止别人修改它。

      在这个隔离级别上;读取了一条数据,这个事务不结束,别的事务就不可以改这条记录;事务提交之后才会释放共享锁

      它的优势是可以解决脏读,重复读问题,它的劣势是仍然会产生幻读的问题

      适用场景:不要在更新时间长的场景下使用此隔离级别

    幻读:在一个事务里面的操作中发现了未被操作的数据;幻读出现的前提是并发的事务中有事务发生了插入、删除操作;实际上更新操作也可以造成幻读

    示例:storeusers表,在sql server中开启两个事务,事务A先启动,事务A会在第一个读取后等待9秒;事务B后启动;事务B等待4秒,相同过滤条件下事务A前后两次读取的数据量发生了变化;这是一个【幻读】

--------幻读演示事务A
begin transaction
select age from SortUsers where age>8998;
waitfor delay '00:00:09:000'
select age from SortUsers where age>8998;
commit

  

----幻读演示事务B
begin transaction
select age from SortUsers where age>8998;
INSERT INTO [dbo].[SortUsers]([Id],[Name],[Code],[Desciption],[Age],[UserId])
     VALUES('C19000','C19000','C19000','C19000',9000,'C19000')
waitfor delay '00:00:04:000'
commit
select age from SortUsers where age>8998;

  

  4),Serializabale(序列化):将数据库中所有事务以串联的方式连接起来执行,防止一个事务影响其他事务

    这个隔离级别使用的是[区间锁]的原理来实现的;对某个区域的数据进行加锁

    它的优势是可以解决脏读,重复读,幻读问题;其保证了一个事务不会读到另一个并行事务已修改但未提交的数据,它的劣势是没有并发事务;性能差,并发差

    适用场景:罕见

  5),默认隔离级别:SQLServer默认使用Read Committed;Mysql默认使用Repeatable read

  6),SnapShot:使用快照,快照使用的是乐观锁机制,使用TempDB来实现,将要操作的数据创建副本,从而数据隔离开来

    从而避免脏读,幻读,重复读;

    它的优势是有很好的隔离性,仍然有并发性,它的劣势是因为使用TempDB,IO性能将会下降;对并发性的支持也是有瓶颈

  

  事务:可以认为是一组操作的集合,事务的具有ACID四个特性

  持久性(durability);事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响

  一致性(consistency);事务必须是使数据库从一个一致性状态变到另一个一致性状态,并且不违反任何一致性约束

  原子性(atomicity):一个事务是一个不可分割的工作单位,事务中的操作要么都做,要么都不做;

  隔离性(isolation):一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰

锁:事务使用锁和隔离级别来实现事务的四个特性;但是锁的特性也影响到事务的性能和并发性;事务执行中的死锁,并发问题是锁常见的问题

会话:一个数据库链接就是一个会话;每一个会话都有一个唯一的SessionID;事务是和会话相关联的;会话在成功接入sqlserver之后,事务就会在某个隔离级别上运行

阻塞:阻塞指的是至少两个事务中,一个事务被另一个事务阻塞的现象

 

锁:

  行锁:粒度最小的锁级别,行锁是针对指定的行进行加锁;根据行是否有聚集索引,行锁可以分为键值锁和行标识锁

  页锁:针对数据页的加锁,查询使用了行锁时,不会对数据使用页锁;使用页锁时,也不会使用行锁

  表锁:对整个表加锁,表锁包含的数据页和数据行上此时都无法加锁

  锁的模式:

    排他锁,也称X锁;写操作,增删改会对受影响的行加X锁,同一时刻只有一个事务能持有对统一对象的X锁;其他事务需要等待

      X锁的释放才能操作;事务的隔离级别不影响X锁的行为

    共享锁,也称S锁;读操作,多个事务可以同时访问S锁上的资源,共享锁是受事务隔离级别的影响

    意向锁,也称I锁;放置在资源层次结构的一个级别上的锁,以保护较低级别资源上的共享锁或排他锁

    更新锁,也称U锁,在数据修改中,可能需要先查询再更新,使用U锁对查询出来的数据进行加锁,告诉其他事务这些数据即将被更新;

      加锁成功之后,再将U锁升级到X锁,然后再执行更新

  锁的兼容性:锁是否兼容,会影响事务之间的阻塞;兼容的锁,事务之间的操作不会阻塞,典型的S锁;不兼容的锁,则会造成事务之间的阻塞

    典型的X锁,X锁和任何锁都不兼容

    锁的兼容性图

    

  

  运行时查看当前锁状况:sqlserver中,使用系统函数sp_lock查看锁的状况

  

    自定义查看某个事务的锁:

-----在目标数据库创建一个名叫DBlocks的视图用于查看引擎中的锁
Use Foundation---你的数据库名字
go
IF EXISTS ( SELECT  1 FROM  sys.views WHERE   name = 'DBlocks' )
DROP VIEW DBlocks ;
GO
CREATE VIEW DBlocks 
AS
SELECT  request_session_id AS spid ,
DB_NAME(resource_database_id) AS dbname ,
CASE WHEN resource_type = 'OBJECT'
THEN OBJECT_NAME(resource_associated_entity_id)
WHEN resource_associated_entity_id = 0 THEN 'N/A'
ELSE OBJECT_NAME(p.object_id)
END AS entity_name , index_id , resource_type AS resource ,
        resource_description AS description ,
        request_mode AS mode , request_status AS status
FROM    sys.dm_tran_locks t LEFT JOIN sys.partitions p
ON p.partition_id = t.resource_associated_entity_id
WHERE   resource_database_id = DB_ID()
AND resource_type <> 'DATABASE' ;

    在建立DBBlocks视图后,就可以在DBBlocks查看引擎中的锁状况;如下是查询当前执行的事务的锁

    

    

    阻塞:当一个事务的执行需要等待另一个事务执行释放资源时,便是阻塞

      示例:Company4表,一个事务执行对指定行进行更新,但是这个事务就是不提交也不回滚;此时另外一个查询窗口去查询这行数据时,就会一直在等待

    

    定位阻塞事务的锁

      示例:

        1,先启动引起阻塞的事务A      

        2,启动查询B查询被A阻塞的资源,启动后,查询B一直处于执行状态

        3,启动脚本[前面所创建的DBBlocks视图]查询当前查询B的会话Id(查询B是一个S锁);再查询查询B的阻塞信息

          下面这个图演示了会话53被会话52给阻塞了,X锁不兼容S锁(X锁不兼容任何锁)

------第一步:事务A,启动后不提交也不回滚
begin transaction
update Company4 set [Description]='贝蒂哈使'
where Id='C13192'
-----第二步,启动查询B查询被A阻塞的资源,启动后,查询B一直处于执行状态
select [Description] from Company4 where Id='C13192'

 

----查询实时的阻塞情况
-- 实时侦测阻塞
select * from DBlocks;
-- 锁申请及其状态
select session_id,wait_time,wait_type,blocking_session_id 
from sys.dm_exec_requests  where session_id=53;----53是当前被阻塞会话ID
select session_id,wait_duration_ms,wait_type,
blocking_session_id,resource_description 
from sys.dm_os_waiting_tasks where session_id=53;----53是当前被阻塞会话ID

  

  你也可以使用以下语句来查看当前系统所有的更多的阻塞信息(包括执行脚本和执行计划等);而且也无需知道会话的Id  

select 
    tl1.resource_type as [Resource Type] 
    ,db_name(tl1.resource_database_id) as [DB Name] 
    ,case tl1.resource_type 
when  'OBJECT' then object_name(tl1.resource_associated_entity_id
,tl1.resource_database_id) 
        when 'DATABASE' then 'DB' 
        else 
            case when tl1.resource_database_id = db_id() 
                then 
                     (  select object_name(object_id, tl1.resource_database_id) 
                        from sys.partitions 
                        where hobt_id = tl1.resource_associated_entity_id ) 
                else '(Run under DB context)' 
            end 
    end as [Object] 
    ,tl1.resource_description as [Resource] 
    ,tl1.request_session_id as [Session] 
    ,tl1.request_mode as [Mode] 
    ,tl1.request_status as [Status] 
    ,wt.wait_duration_ms as [Wait (ms)] 
	   ,qi.sql 
    ,qi.query_plan 
from 
    sys.dm_tran_locks tl1 with (nolock) join sys.dm_tran_locks tl2 with (nolock) on 
        tl1.resource_associated_entity_id = tl2.resource_associated_entity_id 
    left outer join sys.dm_os_waiting_tasks wt with (nolock) on 
        tl1.lock_owner_address = wt.resource_address and tl1.request_status = 'WAIT' 
    outer apply 
    ( 
        select 
            substring(s.text, (er.statement_start_offset / 2) + 1, 
                 ((  case er.statement_end_offset 
                            when -1 
                            then datalength(s.text) 
                            else er.statement_end_offset 
                      end - er.statement_start_offset) / 2) + 1) as sql 
            , qp.query_plan 
        from 
            sys.dm_exec_requests er with (nolock) 
                cross apply sys.dm_exec_sql_text(er.sql_handle) s 
                outer apply sys.dm_exec_query_plan(er.plan_handle) qp 
        where 
            tl1.request_session_id = er.session_id 
    ) qi 
where 
    tl1.request_status <> tl2.request_status and 
    ( 
        tl1.resource_description = tl2.resource_description or 
        ( tl1.resource_description is null and tl2.resource_description is null ) 
    ) 
option (recompile)

    

 

   总结:

      1,阻塞发生再多个会话/事务的资源争夺

      2,如果没有索引;即使是更新一行数据,可能会导致锁升级,也可能导致锁定大量的数据

      3,确保事务及时提交,X锁总是在事务结束后才释放,缩短事务执行时间

      4,更新操作尽可能靠近事务的结尾

      5,避免在一个事务中多次更新相同的数据,如多个Update更新相同数据的不同字段,这会增加锁的数量和开销

      6,监控锁升级

      7,同一个事务中,不要混用DML和DDL;如DDL修改表结构,会导致部分sql重新编译,统计信息无效,执行计划重新编译,执行缓存清楚

      8,不要疯狂使用with nolock;with nolock不一定能够解决写操作导致的阻塞;同时可能脏读脏到用户无法接受的程度

      9,事务和锁并没有大大的提升数据库性能

      10,数据库本身不是为了性能而存在,数据库的一致性才是数据库的事务的前提

  In-Memory(IMDB)简介:sql server2014+版本

    在计算机中,

网络的IO速度慢于磁盘的IO速度,

磁盘的IO速度慢于内存的访问速度,

内存的访问速度慢于cpu三级缓存的访问速度

cpu三级缓存访问速度慢于cpu二级缓存的访问速度

cpu二级缓存访问速度慢于cpu一级缓存的访问速度

cpu一级缓存访问速度慢于cpu计算速度

    传统的关系数据库和现在的redis,mecached等缓存技术相比;

      传统的关系型数据库大部分是磁盘IO;它们通过将访问的数据加载到内存中进行查询匹配等操作,当内存不足时,则将大量的内存中所需的数据转移到磁盘上的TempDB(这是典型的内存磁盘数据交换)

      而磁盘的IO速度是远远不如内存;这就是磁盘表和内存表

    redis,mecached等缓存技术则更多是运行在内存中,内存IO的速度是远远高于磁盘IO的

In-Mempory Database;即内存数据库,是一种依赖于主存作为数据存储介质的一种数据库管理系统;

内存优化表,存储在内存中的表,它也有索引,包括散列索引和范围索引,而且至少要有一个索引;最多8个索引;除主键外没有唯一索引;

  创建之后不可以修改;建表的时候就要定义所有的索引

  默认情况下,内存优化表具有完全持久性。与(传统)基于磁盘的表上的事务一样,内存优化表上的事务具有完全原子性、一致性、隔离性和持久性 (ACID)。

   内存优化表和本机编译的存储过程仅支持一部分 Transact-SQL 功能

 

  分库分表属于架构级的,在此不讨论

标签:事务,resource,where,数据库,性能,tl1,优化,id,select
来源: https://www.cnblogs.com/cmliu/p/13661783.html