zoukankan      html  css  js  c++  java
  • Dynamics CRM 2011 数据库清理

    长时间运行之后,由于一些workflow在设定的时候没有选择删除运行记录,

    image

    长期运行下来导致AsyncOperationBase 和WorkflowLogBase保留很多历史运行记录,影响系统效率。

    这时候使用下面的script在特定组织数据库下运行就可以删除掉那些运行记录了

    IF EXISTS (SELECT name from sys.indexes
                      WHERE name = N'CRM_AsyncOperation_CleanupCompleted')
          DROP Index AsyncOperationBase.CRM_AsyncOperation_CleanupCompleted
    GO
    CREATE NONCLUSTERED INDEX CRM_AsyncOperation_CleanupCompleted
    ON [dbo].[AsyncOperationBase] ([StatusCode],[StateCode],[OperationType])
    GO
    
    while(1=1)
    begin
     declare @DeleteRowCount int = 10000
     declare @rowsAffected int
     declare @DeletedAsyncRowsTable table (AsyncOperationId uniqueidentifier not null primary key)
     insert into @DeletedAsyncRowsTable(AsyncOperationId)
     Select top (@DeleteRowCount) AsyncOperationId from AsyncOperationBase
     where 
      OperationType in (1, 9, 12, 25, 27, 10) 
      AND StateCode = 3 
      AND StatusCode in (30, 32)
     
     select @rowsAffected = @@rowcount 
     delete poa from PrincipalObjectAccess poa 
       join WorkflowLogBase wlb on
        poa.ObjectId = wlb.WorkflowLogId
       join @DeletedAsyncRowsTable dart on
        wlb.AsyncOperationId = dart.AsyncOperationId
     delete WorkflowLogBase from WorkflowLogBase W, @DeletedAsyncRowsTable d
     where 
      W.AsyncOperationId = d.AsyncOperationId             
     delete BulkDeleteFailureBase From BulkDeleteFailureBase B, @DeletedAsyncRowsTable d
     where 
      B.AsyncOperationId = d.AsyncOperationId
     delete WorkflowWaitSubscriptionBase from WorkflowWaitSubscriptionBase WS, @DeletedAsyncRowsTable d
     where 
      WS.AsyncOperationId = d.AsyncOperationID 
     delete AsyncOperationBase From AsyncOperationBase A, @DeletedAsyncRowsTable d
     where 
      A.AsyncOperationId = d.AsyncOperationId
     /*If not calling from a SQL job, use the WAITFOR DELAY*/
     if(@DeleteRowCount > @rowsAffected)
      return
     else
      WAITFOR DELAY '00:00:02.000'
    end

    同时PrincipalObjectAccess 也会增大很多用户访问数据,这些也需要经常清理一下,利用下面的script

    BEGIN TRY
    BEGIN TRAN t1 
    
    
    IF NOT EXISTS (SELECT * FROM sys.sysobjects
    
    WHERE id = object_id(N'[dbo].[ToDeletePOAEntries]')
    
    AND ObjectProperty(id, N'IsUserTable') = 1)
    
    
    create table ToDeletePoaEntries
    (
    ObjectId uniqueidentifier,
    Otc int
    )
    
    
    IF NOT EXISTS (SELECT * FROM sys.sysindexes si
    
    INNER JOIN sys.sysobjects so ON si.id = so.id
    
    WHERE so.id = OBJECT_ID(N'[dbo].[ToDeletePoaEntries]')
    
    AND OBJECTPROPERTY(so.id, N'IsUserTable') = 1
    
    AND si.name LIKE '%mainindex%')
    
    
    CREATE UNIQUE NONCLUSTERED INDEX [mainindex] ON [dbo].[ToDeletePoaEntries] 
    (
    [ObjectId] ASC,
    [Otc] ASC
    )WITH (STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    
    ----- Insert records to be deleted in ToDeletePoaEntries
    
    -- go through all user-owned entities which are not replicated and don't support duplicate detection
    declare entity_cursor cursor local FORWARD_ONLY READ_ONLY
    for select distinct e.ObjectTypeCode, e.BaseTableName, a.PhysicalName from EntityView e
    inner join AttributeView a on e.EntityId = a.EntityId and a.IsPKAttribute = 1
    where e.IsReplicated = 0 and e.IsDuplicateCheckSupported = 0 and e.OwnershipTypeMask & 1 = 1
    
    open entity_cursor
    
    declare @baseTableName sysname
    declare @otc nvarchar(20)
    declare @primaryKey sysname
    declare @totalCollected int = 0
    declare @currentCollected int
    
    declare @tempRowCount int = 0
    declare @collectstatement nvarchar(max)
    
    fetch next from entity_cursor
    into @otc, @baseTableName, @primaryKey
    
    while @@FETCH_STATUS = 0
    begin
    print 'Cleaning up POA for ' + @baseTableName
    
    set @currentCollected = 0
    
    
    
    set @collectstatement = 'insert into ToDeletePoaEntries(ObjectId, Otc) 
    select distinct poa.ObjectId, poa.ObjectTypeCode 
    from PrincipalObjectAccess poa
    left join ' + @baseTableName + ' e on poa.ObjectId = e.' + @primaryKey + 
    ' where e.' + @primaryKey + ' is null and poa.ObjectTypeCode = ' + @otc;
    
    print @collectstatement
    
    exec(@collectstatement)
    set @tempRowCount = @@ROWCOUNT
    set @currentCollected = @currentCollected + @tempRowCount
    print CAST(@currentCollected as nvarchar(20)) + ' records collected for ' + @baseTableName
    set @totalCollected = @totalCollected + @currentCollected
    
    fetch next from entity_cursor
    into @otc, @baseTableName, @primaryKey
    end
    
    close entity_cursor
    deallocate entity_cursor
    
    print CAST(@totalCollected as nvarchar(20)) + ' total records collected'
    
    -- Delete query
    
    
    -- This scripts cleans up orphaned POA records for selected entities
    
    declare @deleteBatchSize int = 50000
    declare @deleteBatchSizeNVarChar nvarchar(10) = CAST(@deleteBatchSize as nvarchar(10))
    declare @totalDeleted int = 0
    declare @currentDeleted int
    declare @deletestatement nvarchar(max)
    
    set @currentDeleted = 0
    set @tempRowCount = 0
    
    -- delete all records of the current entity type which don't have corresponding object in the base table
    set @deletestatement = 'delete top (' + @deleteBatchSizeNVarChar + ') from PrincipalObjectAccess
    from PrincipalObjectAccess poa
    join ToDeletePoaEntries e on poa.ObjectId = e.ObjectId and poa.ObjectTypeCode = e.Otc'
    
    print @deletestatement
    
    -- delete PrincipalObjectAccess records in batches
    exec(@deletestatement)
    set @tempRowCount = @@ROWCOUNT
    set @currentDeleted = @currentDeleted + @tempRowCount
    
    while @tempRowCount = @deleteBatchSize
    begin
     exec(@deletestatement)
     set @tempRowCount = @@ROWCOUNT
     set @currentDeleted = @currentDeleted + @tempRowCount
    
    print CAST(@currentDeleted as nvarchar(20)) + ' records deleted ' + cast(getUtcdate() as nvarchar(50))
    --cleanup
    
    end
    
    COMMIT TRAN t1
    
    -- Cleanup
    
    DROP Table [dbo].[ToDeletePoaEntries]
    PRINT 'EXECUTION SUCCEED'
    END TRY
    BEGIN CATCH
    ROLLBACK TRAN t1
    
    -- Cleanup
    
    DROP Table [dbo].[ToDeletePoaEntries]
    
    PRINT 'EXECUTION FAILED :' + ERROR_MESSAGE()
    END CATCH

    做完清理之后,重建一下index可以更好的利用缓存

    ALTER INDEX ndx_for_cascaderelationship_lk_workflowlog_asyncoperations ON WorkflowLogBase REBUILD
    ALTER INDEX ndx_Security ON WorkflowLogBase REBUILD
    ALTER INDEX ndx_Cover ON PrincipalObjectAccess REBUILD
    ALTER INDEX ndx_PrimaryKey_PrincipalObjectAccess ON PrincipalObjectAccess REBUILD
    ALTER INDEX cndx_PrincipalObjectAccess ON PrincipalObjectAccess REBUILD
    ALTER INDEX fndx_Sync_VersionNumber ON PrincipalObjectAccess REBUILD
    ALTER INDEX UQ_PrincipalObjectAccess ON PrincipalObjectAccess REBUILD
    ALTER INDEX cndx_PrimaryKey_AsyncOperation ON AsyncOperationBase REBUILD
    ALTER INDEX ndx_SystemManaged_AsyncOperation ON AsyncOperationBase REBUILD

    这些动作还是要经常进行的,最好做成sql agent job然后每个月运行

    检查db_buffer_pages使用情况

    -- Note: querying sys.dm_os_buffer_descriptors
    -- requires the VIEW_SERVER_STATE permission.
    
    DECLARE @total_buffer INT;
    
    SELECT @total_buffer = cntr_value
    FROM sys.dm_os_performance_counters 
    WHERE RTRIM([object_name]) LIKE '%Buffer Manager'
    AND counter_name = 'Database Pages';
    
    ;WITH src AS
    (
    SELECT 
    database_id, db_buffer_pages = COUNT_BIG(*)
    FROM sys.dm_os_buffer_descriptors
    --WHERE database_id BETWEEN 5 AND 32766
    GROUP BY database_id
    )
    SELECT
    [db_name] = CASE [database_id] WHEN 32767 
    THEN 'Resource DB' 
    ELSE DB_NAME([database_id]) END,
    db_buffer_pages,
    db_buffer_MB = db_buffer_pages / 128,
    db_buffer_percent = CONVERT(DECIMAL(6,3), 
    db_buffer_pages * 100.0 / @total_buffer)
    FROM src
    ORDER BY db_buffer_MB DESC;

    检查index的空闲空间,需要rebuild

    EXEC sp_MSforeachdb
        N'IF EXISTS (SELECT 1 FROM (SELECT DISTINCT DB_NAME ([database_id]) AS [name]
        FROM sys.dm_os_buffer_descriptors) AS names WHERE [name] = ''?'')
    BEGIN
    USE [?]
    SELECT
        ''?'' AS [Database],
        OBJECT_NAME (p.[object_id]) AS [Object],
        p.[index_id],
        i.[name] AS [Index],
        i.[type_desc] AS [Type],
        --au.[type_desc] AS [AUType],
        --DPCount AS [DirtyPageCount],
        --CPCount AS [CleanPageCount],
        --DPCount * 8 / 1024 AS [DirtyPageMB],
        --CPCount * 8 / 1024 AS [CleanPageMB],
        (DPCount + CPCount) * 8 / 1024 AS [TotalMB],
        --DPFreeSpace / 1024 / 1024 AS [DirtyPageFreeSpace],
        --CPFreeSpace / 1024 / 1024 AS [CleanPageFreeSpace],
        ([DPFreeSpace] + [CPFreeSpace]) / 1024 / 1024 AS [FreeSpaceMB],
        CAST (ROUND (100.0 * (([DPFreeSpace] + [CPFreeSpace]) / 1024) / (([DPCount] + [CPCount]) * 8), 1) AS DECIMAL (4, 1)) AS [FreeSpacePC]
    FROM
        (SELECT
            allocation_unit_id,
            SUM (CASE WHEN ([is_modified] = 1)
                THEN 1 ELSE 0 END) AS [DPCount],
            SUM (CASE WHEN ([is_modified] = 1)
                THEN 0 ELSE 1 END) AS [CPCount],
            SUM (CASE WHEN ([is_modified] = 1)
                THEN CAST ([free_space_in_bytes] AS BIGINT) ELSE 0 END) AS [DPFreeSpace],
            SUM (CASE WHEN ([is_modified] = 1)
                THEN 0 ELSE CAST ([free_space_in_bytes] AS BIGINT) END) AS [CPFreeSpace]
        FROM sys.dm_os_buffer_descriptors
        WHERE [database_id] = DB_ID (''?'')
        GROUP BY [allocation_unit_id]) AS buffers
    INNER JOIN sys.allocation_units AS au
        ON au.[allocation_unit_id] = buffers.[allocation_unit_id]
    INNER JOIN sys.partitions AS p
        ON au.[container_id] = p.[partition_id]
    INNER JOIN sys.indexes AS i
        ON i.[index_id] = p.[index_id] AND p.[object_id] = i.[object_id]
    WHERE p.[object_id] > 100 AND ([DPCount] + [CPCount]) > 12800 -- Taking up more than 100MB
    ORDER BY [FreeSpacePC] DESC;
    END';

    检查index的缓存大小

    ;WITH src AS
    (
    SELECT
    [Object] = o.name,
    [Type] = o.type_desc,
    [Index] = COALESCE(i.name, ''),
    [Index_Type] = i.type_desc,
    p.[object_id],
    p.index_id,
    au.allocation_unit_id
    FROM
    sys.partitions AS p
    INNER JOIN
    sys.allocation_units AS au
    ON p.hobt_id = au.container_id
    INNER JOIN
    sys.objects AS o
    ON p.[object_id] = o.[object_id]
    INNER JOIN
    sys.indexes AS i
    ON o.[object_id] = i.[object_id]
    AND p.index_id = i.index_id
    WHERE
    au.[type] IN (1,2,3)
    AND o.is_ms_shipped = 0
    )
    SELECT
    src.[Object],
    src.[Type],
    src.[Index],
    src.Index_Type,
    buffer_pages = COUNT_BIG(b.page_id),
    buffer_mb = COUNT_BIG(b.page_id) / 128
    FROM
    src
    INNER JOIN
    sys.dm_os_buffer_descriptors AS b
    ON src.allocation_unit_id = b.allocation_unit_id
    WHERE
    b.database_id = DB_ID()
    GROUP BY
    src.[Object],
    src.[Type],
    src.[Index],
    src.Index_Type
    ORDER BY
    buffer_mb DESC;

    当然最后还要重新整理一下缓存

    DBCC FREESYSTEMCACHE ('ALL')
    DBCC FREESESSIONCACHE
    DBCC FREEPROCCACHE
    
    
    EXEC sys .sp_configure N'show advanced options', N'1' RECONFIGURE WITH OVERRIDE
    GO
    EXEC sys .sp_configure N'max server memory (MB)', N'4000'
    GO
    RECONFIGURE WITH OVERRIDE
    GO
    EXEC sys .sp_configure N'show advanced options', N'0' RECONFIGURE WITH OVERRIDE
    GO
    
    
    EXEC sys .sp_configure N'show advanced options', N'1' RECONFIGURE WITH OVERRIDE
    GO
    EXEC sys .sp_configure N'max server memory (MB)', N'11000'
    GO
    RECONFIGURE WITH OVERRIDE
    GO
    EXEC sys .sp_configure N'show advanced options', N'0' RECONFIGURE WITH OVERRIDE
    GO
  • 相关阅读:
    Delphi集合的用法
    文字倒序输出(集合)
    如果知道两点的经纬度 如何算两点之间的距离
    Arcengine 开发完后,程序打包,在目标机器上不能使用 已解决
    arcengine License部署
    设置代理
    关于GPS坐标转换的学习笔记相当头疼
    ArcEngine 相关转载
    经纬度到平面坐标的相互转换
    用ArcEngine9.3开发GIS应用程序图层符号化解决方案
  • 原文地址:https://www.cnblogs.com/yeung/p/5110910.html
Copyright © 2011-2022 走看看