ICode9

精准搜索请尝试: 精确搜索
首页 > 数据库> 文章详细

数据库性能优化4

2020-09-13 22:31:47  阅读:192  来源: 互联网

标签:事务 resource where 数据库 性能 tl1 优化 id select


概述

  隔离级别

  锁

  事务

  总结

  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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有