Database
 sql >> база данни >  >> RDS >> Database

Предикатният ред има значение при разширени събития

Във всяка презентация, която правя за разширени събития, се опитвам да обясня една от най-големите разлики между филтриране в разширени събития и филтриране в Trace; фактът, че редът на предикатите има значение в разширените събития. През повечето време говоря за късо съединение на оценката на предиката в разширени събития и се опитвам да накарам предиката на събитието да се провали при логическата оценка възможно най-бързо, за да върне контрола към изпълняващата задача. Наскоро работех с една от моите примерни сесии за събития, които използвам в презентации, която демонстрира друг важен аспект на реда на предикатите в разширените събития.

В рамките на разширените събития има текстови предикатни компаратори, които позволяват по-сложни дефиниции на критериите за филтриране за дадено събитие. Някои от тях всъщност поддържат вътрешно състояние, докато сесията на събитието се стартира на сървъра, например компараторите package0.greater_than_max_uint64 и package0.less_than_min_uint64. Съществува и предикатен изходен елемент, package0.counter, който също поддържа вътрешно състояние при стартиране на сесията на събитието. За предикатите, поддържащи състоянието в разширени събития, едно от най-важните съображения е, че вътрешното състояние се променя всеки път, когато се оценява предиката, поддържащ състоянието, а не когато събитието се задейства напълно. За да демонстрираме това, нека да разгледаме примерна употреба на пакета за сравнение на текстови предикати package0.greater_than_max_uint64. Първо ще трябва да създадем съхранена процедура, която да контролираме продължителността на изпълнение:

ИЗПОЛЗВАЙТЕ AdventureWorks2012GOIF OBJECT_ID(N'StoredProcedureExceedsDuration') НЕ Е НУЛОВА ПРОЦЕДУРА ИЗПУСКАНЕ dbo.StoredProcedureExceedsDuration;GOCREATE PROCEDURE dbo.StoredProcedureExceedsDuration') НЕ Е NULL ПРОЦЕДУРА ЗА ИЗПУСКАНЕ dbo.StoredProcedureExceedsDuration;GOCREATE PROCEDURE dbo.StoredProcedureExceedsDuration')(@StoredProcedureExceedsDuration') :0 0 0 0 0 0 0 0 0 0 0 0 0 ОТПРАВИ

След това ще трябва да създадем сесия на събитие, за да проследим изпълнението на съхранената процедура с помощта на събитието sqlserver.module_end и да филтрираме изпълненията в колоните object_id и source_database_id, предоставени от събитието. Ние също така ще дефинираме филтър с помощта на текстовия компаратор package0.greater_than_max_uint64 спрямо колоната за продължителност, която е в микросекунди в разширени събития, с първоначално състояние от 1000000 или една секунда. С това допълнение към предиката събитието ще се задейства само когато продължителността надвиши първоначалната стойност от 1000000 микросекунди, а след това предикатът ще съхрани стойността на новото състояние вътрешно, така че събитието да не се задейства отново напълно, докато продължителността не надвиши стойност на новото вътрешно състояние. След като създадем сесията на събитието, която в този случай използва Dynamic SQL, тъй като не можем да използваме параметризация в DDL изрази в SQL Server, тя ще бъде стартирана на сървъра и можем да изпълним нашата примерна съхранена процедура и да контролираме продължителността на изпълнение няколко пъти за да разгледаме как събитието се задейства с нашия предикат.

АКО СЪЩЕСТВУВА(SELECT * FROM sys.server_event_sessions WHERE name='StatementExceedsLastDuration') ИЗПУСКАНЕ НА СЪБИТИЯТА СЕСИЯ [StatementExceedsLastDuration] НА СЪРВЪР; GO-- Създайте сесията на събитието, използвайки динамичен SQL за конкатениране на database_id -- и object_id в DDL, параметризацията не е разрешена в DDL!DECLARE @ObjectID NVARCHAR(10) =OBJECT_ID('StoredProcedureExceedsDuration'), @DatabaseID NVARCHAR(10) =DB_ID('AdventureWorks2012');DECLARE @SqlCmd NVARCHAR(MAX) ='СЪЗДАВАНЕ НА СЕСИЯ НА СЪБИТИЕ [StatementExceedsLastDuration] НА СЪБИТИЕ НА SERVERADD sqlserver.module_end( SET collect_statement_ (da идентификатор на обекта =1 WHER_ID' +Object_ID) =1 WHER_AND DatabaseID + ' И package0.greater_than_max_uint64(продължителност, 1000000)))ДОБАВЕТЕ ЦЕЛ package0.ring_buffer(SET max_events_limit=10);' EXECUTE(@SqlCmd) ПРОМЕНИ СЪБИТИЯТА СЕСИЯ [StatementExceedsLastDuration]ON SERVERSTATE=СТАРТ; Execute AdventureWorks2012.dbo.storedProcedureExceedSduration; Execute AdventureWorks2012.dbo.storedProcedureExcepeadsduration '00:00:01.000 '; Execute AdventureWorks2012.dbo.storedProcedureExceduration; AdventureWorks2012.dbo.StoredProcedureExceedsDuration '00:00:01.000';EXECUTE AdventureWorks2012.dbo.StoredProcedureExceedsDuration;

Ако четете моя блог на SQLskills.com, вероятно знаете, че не съм голям фен на използването на целта ring_buffer в разширени събития по редица причини. За това ограничено събиране на данни и факта, че сесията на събитието го ограничава до максимум десет събития, е лесна цел да се демонстрира поведението на реда предикатите на събитието, но все пак трябва да раздробим XML за събитията ръчно, за да вижте резултатите.

-- Изтрийте събитията от targetSELECT event_data.value('(@name)[1]', 'nvarchar(50)') AS event_name, event_data.value('(@timestamp)[1]', ' datetime2') AS [timestamp], event_data.value('(data[@name="duration"]/value)[1]', 'bigint') като продължителност, event_data.value('(data[@name=" израз"]/стойност)[1]', 'varchar(max)') като [изявление]FROM ( SELECT CAST(target_data AS xml) КАТО TargetData FROM sys.dm_xe_sessions AS s INNER JOIN sys.dm_xe_session_targets AS t ON s.ad =t.event_session_address WHERE s.name =N'StatementExceedsLastDuration' AND t.target_name =N'ring_buffer' ) КАТО tabCROSS APPLY TargetData.nodes (N'RingBufferTarget/event') КАТО evts(event>_data); 

Изпълнението на кода по-горе ще доведе до само 2 събития, едното за една секунда, а след това другото за изпълнение на две секунди. Другите изпълнения на съхранената процедура са по-кратки от първоначалния филтър за продължителност от една секунда, посочен в микросекунди в предиката, а след това последното едно второ изпълнение е по-кратко от съхранената стойност на състоянието от две секунди от компаратора. Това е очакваното поведение на колекцията от събития, но ако променим реда на предикатите, така че филтърът package0.greater_than_max_uint64(duration, 1000000) да се появи първи в реда на предикатите и да създадем втора съхранена процедура, която изпълняваме с продължителност от три секунди първо, няма да получим никакви събития.

ИЗПОЛЗВАЙТЕ AdventureWorks2012GOIF OBJECT_ID(N'StoredProcedureExceedsDuration') НЕ Е НУЛОВА ПРОЦЕДУРА ИЗПУСКАНЕ dbo.StoredProcedureExceedsDuration;GOCREATE PROCEDURE dbo.StoredProcedureExceedsDuration') НЕ Е NULL ПРОЦЕДУРА ЗА ИЗПУСКАНЕ dbo.StoredProcedureExceedsDuration;GOCREATE PROCEDURE dbo.StoredProcedureExceedsDuration')(@StoredProcedureExceedsDuration') :0 0 0 0 0 0 0 0 0 0 0 0 0 GOIF OBJECT_ID(N'StoredProcedureExceedsDuration2') НЕ Е НУЛЕВА ПРОЦЕДУРА ИЗПУСКАНЕ dbo.StoredProcedureExceedsDuration2;GOCREATE PROCEDURE dbo.StoredProcedureExceedsDuration2( @WaitForValue)( @WaitForValue)(@WaitForValue)0:00:00:00:00:00:00 GOIF СЪЩЕСТВУВА(SELECT * FROM sys.server_event_sessions WHERE name='StatementExceedsLastDuration') ИЗПУСКАНЕ НА СЪБИТИЯТА СЕСИЯ [StatementExceedsLastDuration] НА СЪРВЪР; GO-- Създайте сесията на събитието, използвайки динамичен SQL за конкатениране на database_id -- и object_id в DDL, параметризацията не е разрешена в DDL!DECLARE @ObjectID NVARCHAR(10) =OBJECT_ID('StoredProcedureExceedsDuration'), @DatabaseID NVARCHAR(10) =DB_ID('AdventureWorks2012');DECLARE @SqlCmd NVARCHAR(MAX) ='СЪЗДАВАНЕ НА СЕСИЯ НА СЪБИТИЕ [StatementExceedsLastDuration] НА СЪБИТИЕ НА SERVERADD sqlserver.module_end( SET collect_statement (настройка на collect_statement =1 WHER_0_0_0_0_0_0_0_______) ObjectID + ' И source_database_id =' + @DatabaseID + '))ДОБАВЯНЕ НА ЦЕЛ package0.ring_buffer(SET max_events_limit=10);' EXECUTE(@SqlCmd) ПРОМЕНИ СЪБИТИЯТА СЕСИЯ [StatementExceedsLastDuration]ON SERVERSTATE=СТАРТ; Execute AdventureWorks2012.dbo.storedProcedureExcepteSduration; Execute AdventureWorks2012.dbo.storedprocedureExcepeadsduration2 '00:00:03.050 '; Execute AdventureWorks2012.dbo.storedProcedureExceduration' :00:02.050';ИЗПЪЛНЕТЕ AdventureWorks2012.dbo.StoredProcedureExceedsDuration;

В този случай, тъй като компараторът за поддържане на състоянието се появява първи в реда на предиката, неговата вътрешна стойност се увеличава с трите секунди изпълнение на втората съхранена процедура, въпреки че събитието по-късно се проваля във филтъра object_id на предиката и не се задейства напълно. Това поведение се случва с всяко състояние, поддържащо предикати в разширени събития. По-рано открих това поведение с колоната източник на предикат package0.counter, но не разбрах, че поведението се случва за която и да е част от предиката, която поддържа състояние. Вътрешното състояние ще се промени веднага щом тази част от предиката бъде оценена. Поради тази причина всеки предикатен филтър, който променя или поддържа състояние, трябва да бъде абсолютната последна част от дефиницията на предиката, за да се гарантира, че променя състоянието само вътрешно, когато всички предикатни условия са преминали оценка.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Избягване на сортиране с конкатенация на присъединяване при сливане

  2. FrankenQueries:когато SQL и NoSQL се сблъскат

  3. GROUP BY срещу ORDER BY

  4. Intel обречен ли е в пространството на сървърния процесор?

  5. Модел на база данни за онлайн проучване. част 4