Сокращение базы данных до размера - PullRequest
7 голосов
/ 12 апреля 2011

Скажем, у вас есть база данных, которая обслуживала компанию в течение 10 лет.Его размер составляет 500 ГБ, он содержит множество таблиц, хранимых процедур и триггеров.

Теперь предположим, что вы хотите создать урезанную версию базы данных для использования в качестве тестового стенда для использования в интеграционном тестировании и для отдельных тестировщиков.и разработчики раскручивают экземпляры, чтобы поиграться.

В общих чертах, как бы вы решили об этой задаче?

В случае, если это имеет значение, я имею в виду базу данных SQL Server 2008.

Правка: удалено ""тестирование", потому что, конечно, юнит-тесты не должны проверять интеграцию БД

Ответы [ 6 ]

3 голосов
/ 12 апреля 2011

Если все ваши таблицы состоят из несвязанных данных, вы можете просто выбрать X случайных записей из каждой таблицы.Я предполагаю, что проблема в том, что таблицы НЕ связаны, поэтому, если, скажем, таблица A содержит ссылку внешнего ключа на таблицу B, и вы только что извлекли 10% записей из таблицы A и 10% записей из таблицыB, у вас будет целая куча недействительных ссылок от A до B.

Я не знаю общего решения этой проблемы.Это зависит от точной структуры вашей базы данных.Я часто нахожу, что мои базы данных состоят из небольшого количества «центральных» таблиц, которые имеют много ссылок из других таблиц.То есть я обычно нахожу, что у меня есть, скажем, таблица Order, а затем есть таблица Order Line, которая указывает на Order, и таблица Customer, на которую указывает Order, и таблица Delivery, которая указывает на Order или, возможно, Line Order,и т. д., но все, кажется, сосредоточено вокруг "Порядка".В этом случае вы можете случайно выбрать некоторое количество записей о заказах, затем найти всех клиентов для этих заказов, все строки заказов для этих заказов и т. Д. У меня обычно также есть некоторое количество таблиц «поиска кода», например, списоквсе коды "статус заказа", еще один список всех кодов "тип клиента" и т. д. Они обычно невелики, поэтому я просто копирую их полностью.

Если ваша база данных более ... разрознена ..чем это, то есть если у этого нет никаких ясных центров, но является лабиринтом взаимосвязей, это могло бы быть намного более сложным.Я думаю, что тот же принцип будет применяться, хотя.Укажите НЕКОТОРЫЕ отправные точки, выберите несколько записей оттуда, затем получите все записи, связанные с этими записями и т. Д.

3 голосов
/ 12 апреля 2011

Как насчет просмотра файла журнала транзакций? Убедитесь, что вы делаете резервную копию исходной базы данных.

USE db;
GO
-- Truncate the log by changing the database recovery model to SIMPLE.
ALTER DATABASE db
SET RECOVERY SIMPLE;
GO
-- Shrink the truncated log file to 1 MB.
DBCC SHRINKFILE (db_log, 1);
GO
-- Reset the database recovery model.
ALTER DATABASE db
SET RECOVERY FULL;
GO

Я также нашел большой успех в перестройке индексов и дефрагментации .

Тара Кайзер опубликовала это, которое доказало свою эффективность в работе с БД: Спасибо Тара Кизер, если вы прочитали это!

-- required table
IF OBJECT_ID('DefragmentIndexes') IS NULL
    CREATE TABLE DefragmentIndexes
    (
        DatabaseName nvarchar(100) NOT NULL,
        SchemaName nvarchar(100) NOT NULL,
        TableName nvarchar(100) NOT NULL,
        IndexName nvarchar(100) NOT NULL,
        DefragmentDate datetime NOT NULL,
        PercentFragmented decimal(4, 2) NOT NULL,
        CONSTRAINT PK_DefragmentIndexes PRIMARY KEY CLUSTERED 
        (
            DatabaseName,
            SchemaName,
            TableName,
            IndexName,
            DefragmentDate
        )
    )
GO

IF OBJECT_ID(N'[dbo].[isp_ALTER_INDEX]') IS NOT NULL
    DROP PROC [dbo].[isp_ALTER_INDEX]
GO

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-------------------------------------------------------------------------------------------
-- OBJECT NAME           : isp_ALTER_INDEX
--
-- AUTHOR                : Tara Kizer
--
-- INPUTS                : @dbName         - name of the database
--                         @statsMode      - LIMITED, SAMPLED or DETAILED
--                         @defragType     - REORGANIZE (INDEXDEFRAG) or REBUILD (DBREINDEX)
--                         @minFragPercent - minimum fragmentation level
--                         @maxFragPercent - maximum fragmentation level
--                         @minRowCount    - minimum row count
--                         @logHistory     - whether or not to log what got defragmented
--                         @sortInTempdb   - whether or not to sort the index in tempdb;
--                                           recommended if your tempdb is optimized (see BOL for details)
--
-- OUTPUTS               : None
--
-- DEPENDENCIES          : DefragmentIndexes, sys.dm_db_index_physical_stats, sys.objects, sys.schemas, 
--                         sys.indexes, sys.partitions, sys.indexes, sys.index_columns, INFORMATION_SCHEMA.COLUMNS
--
-- DESCRIPTION           : Defragments indexes
/*
                           EXEC isp_ALTER_INDEX 
                             @dbName = 'QHOSClient1', 
                             @statsMode = 'SAMPLED', 
                             @defragType = 'REBUILD', 
                             @minFragPercent = 10,
                             @maxFragPercent = 100,
                             @minRowCount = 1000,
                             @logHistory = 1,
                             @sortInTempdb = 1
*/
/*
    http://weblogs.sqlteam.com/tarad/archive/2009/03/27/Defragmenting-Indexes-in-SQL-Server-2005Again.aspx
        Bug Fix - added SET QUOTED_IDENTIFIER ON to the script
        Feature - added logging feature
    http://weblogs.sqlteam.com/tarad/archive/2009/06/23/DefragmentingRebuilding-Indexes-in-SQL-Server-2005.aspx
        Bug Fix - initialized @lobData to 0 for each pass through the loop
        Bug Fix - checked for LOB data in included columns of non-clustered indexes
        Feature - added SORT_IN_TEMPB option
    http://weblogs.sqlteam.com/tarad/archive/2009/08/31/DefragmentingRebuilding-Indexes-in-SQL-server-2005-and-2008.aspx
        Bug Fix - added index_level = 0 to sys.dm_db_index_physical_stats query
    http://weblogs.sqlteam.com/tarad/archive/2009/11/03/DefragmentingRebuilding-Indexes-in-SQL-Server-2005-and-2008Again.aspx
        Bug Fix - for SQL Server 2008, @indexType could be 'XML INDEX' or 'PRIMARY XML INDEX' for XML indexes
*/ 
-------------------------------------------------------------------------------------------
CREATE PROC [dbo].[isp_ALTER_INDEX]
(
    @dbName sysname, 
    @statsMode varchar(8) = 'SAMPLED', 
    @defragType varchar(10) = 'REORGANIZE', 
    @minFragPercent int = 25, 
    @maxFragPercent int = 100, 
    @minRowCount int = 0,
    @logHistory bit = 0,
    @sortInTempdb bit = 0
)
AS

SET NOCOUNT ON

IF @statsMode NOT IN ('LIMITED', 'SAMPLED', 'DETAILED')
BEGIN
    RAISERROR('@statsMode must be LIMITED, SAMPLED or DETAILED', 16, 1)
    RETURN
END

IF @defragType NOT IN ('REORGANIZE', 'REBUILD')
BEGIN
    RAISERROR('@defragType must be REORGANIZE or REBUILD', 16, 1)
    RETURN
END

DECLARE 
    @i int, @objectId int, @objectName sysname, @indexId int, @indexName sysname, 
    @schemaName sysname, @partitionNumber int, @partitionCount int,
    @sql nvarchar(4000), @edition int, @parmDef nvarchar(500), @allocUnitType nvarchar(60),
    @indexType nvarchar(60), @online bit, @disabled bit, @dataType nvarchar(128),
    @charMaxLen int, @allowPageLocks bit, @lobData bit, @fragPercent float

SELECT @edition = CONVERT(int, SERVERPROPERTY('EngineEdition'))

SELECT 
    IDENTITY(int, 1, 1) AS FragIndexId, 
    [object_id] AS ObjectId, 
    index_id AS IndexId, 
    avg_fragmentation_in_percent AS FragPercent, 
    record_count AS RecordCount, 
    partition_number AS PartitionNumber,
    index_type_desc AS IndexType,
    alloc_unit_type_desc AS AllocUnitType
INTO #FragIndex
FROM sys.dm_db_index_physical_stats (DB_ID(@dbName), NULL, NULL, NULL, @statsMode)
WHERE 
    avg_fragmentation_in_percent > @minFragPercent AND 
    avg_fragmentation_in_percent < @maxFragPercent AND 
    index_id > 0 AND
    index_level = 0
ORDER BY ObjectId

-- LIMITED does not include data for record_count
IF @statsMode IN ('SAMPLED', 'DETAILED')
    DELETE FROM #FragIndex
    WHERE RecordCount < @minRowCount

SELECT @i = MIN(FragIndexId) 
FROM #FragIndex

SELECT 
    @objectId = ObjectId, 
    @indexId = IndexId, 
    @fragPercent = FragPercent,
    @partitionNumber = PartitionNumber,
    @indexType = IndexType,
    @allocUnitType = AllocUnitType
FROM #FragIndex
WHERE FragIndexId = @i

WHILE @@ROWCOUNT <> 0
BEGIN
    -- get the table and schema names for the index
    SET @sql = '
        SELECT @objectName = o.[name], @schemaName = s.[name]
        FROM ' + QUOTENAME(@dbName) + '.sys.objects o
        JOIN ' + QUOTENAME(@dbName) + '.sys.schemas s 
            ON s.schema_id = o.schema_id
        WHERE o.[object_id] = @objectId'

    SET @parmDef = N'@objectId int, @objectName sysname OUTPUT, @schemaName sysname OUTPUT'

    EXEC sp_executesql 
        @sql, @parmDef, @objectId = @objectId, 
        @objectName = @objectName OUTPUT, @schemaName = @schemaName OUTPUT

    -- get index information
    SET @sql = '
        SELECT @indexName = [name], @disabled = is_disabled, @allowPageLocks = allow_page_locks
        FROM ' + QUOTENAME(@dbName) + '.sys.indexes
        WHERE [object_id] = @objectId AND index_id = @indexId'

    SET @parmDef = N'
            @objectId int, @indexId int, @indexName sysname OUTPUT, 
            @disabled bit OUTPUT, @allowPageLocks bit OUTPUT'

    EXEC sp_executesql 
        @sql, @parmDef, @objectId = @objectId, @indexId = @indexId, 
        @indexName = @indexName OUTPUT, @disabled = @disabled OUTPUT, 
        @allowPageLocks = @allowPageLocks OUTPUT

    SET @lobData = 0

    -- for clustered indexes, check for columns in the table that use a LOB data type
    IF @indexType = 'CLUSTERED INDEX'
    BEGIN
        -- CHARACTER_MAXIMUM_LENGTH column will equal -1 for max size or xml
        SET @sql = '
            SELECT @lobData = 1
            FROM ' + QUOTENAME(@dbName) + '.INFORMATION_SCHEMA.COLUMNS c
            WHERE   TABLE_SCHEMA = @schemaName AND
                    TABLE_NAME = @objectName AND
                    (DATA_TYPE IN (''text'', ''ntext'', ''image'') OR 
                    CHARACTER_MAXIMUM_LENGTH = -1)'

        SET @parmDef = N'@schemaName sysname, @objectName sysname, @lobData bit OUTPUT'

        EXEC sp_executesql 
            @sql, @parmDef, @schemaName = @schemaName, @objectName = @objectName, 
            @lobData = @lobData OUTPUT
    END
    -- for non-clustered indexes, check for LOB data type in the included columns
    ELSE IF @indexType = 'NONCLUSTERED INDEX'
    BEGIN
        SET @sql = '
            SELECT @lobData = 1
            FROM ' + QUOTENAME(@dbName) + '.sys.indexes i
            JOIN ' + QUOTENAME(@dbName) + '.sys.index_columns ic
                ON i.object_id = ic.object_id
            JOIN ' + QUOTENAME(@dbName) + '.INFORMATION_SCHEMA.COLUMNS c
                ON ic.column_id = c.ORDINAL_POSITION
            WHERE   c.TABLE_SCHEMA = @schemaName AND
                    c.TABLE_NAME = @objectName AND
                    i.name = @indexName AND
                    ic.is_included_column = 1 AND
                    (c.DATA_TYPE IN (''text'', ''ntext'', ''image'') OR c.CHARACTER_MAXIMUM_LENGTH = -1)'

        SET @parmDef = N'@schemaName sysname, @objectName sysname, @indexName sysname, @lobData bit OUTPUT'

        EXEC sp_executesql 
            @sql, @parmDef, @schemaName = @schemaName, @objectName = @objectName, 
            @indexName = @indexName, @lobData = @lobData OUTPUT
    END

    -- get partition information for the index
    SET @sql = '
        SELECT @partitionCount = COUNT(*)
        FROM ' + QUOTENAME(@dbName) + '.sys.partitions
        WHERE [object_id] = @objectId AND index_id = @indexId'

    SET @parmDef = N'@objectId int, @indexId int, @partitionCount int OUTPUT'

    EXEC sp_executesql 
        @sql, @parmDef, @objectId = @objectId, @indexId = @indexId, 
        @partitionCount = @partitionCount OUTPUT

    -- Developer and Enterprise have the ONLINE = ON option for REBUILD.
    -- Indexes, including indexes on global temp tables, can be rebuilt online with the following exceptions:
    -- disabled indexes, XML indexes, indexes on local temp tables, partitioned indexes,
    -- clustered indexes if the underlying table contains LOB data types (text, ntext, image, varchar(max), 
    -- nvarchar(max), varbinary(max) or xml), and
    -- nonclustered indexes that are defined with LOB data type columns.
    -- When reoganizing and page locks is disabled for the index, we'll switch to rebuild later on, 
    -- so we need to get setup with the proper online option.
    IF @edition = 3 AND (@defragType = 'REBUILD' OR (@defragType = 'REORGANIZE' AND @allowPageLocks = 0))
    BEGIN
        SET @online = 
                CASE
                    WHEN @indexType IN ('XML INDEX', 'PRIMARY XML INDEX') THEN 0
                    WHEN @indexType = 'NONCLUSTERED INDEX' AND @allocUnitType = 'LOB_DATA' THEN 0
                    WHEN @lobData = 1 THEN 0
                    WHEN @disabled = 1 THEN 0
                    WHEN @partitionCount > 1 THEN 0
                    ELSE 1
                END
    END
    ELSE
        SET @online = 0

    -- build the ALTER INDEX statement
    SET @sql = 'ALTER INDEX ' + QUOTENAME(@indexName) + ' ON ' + QUOTENAME(@dbName) + '.' + 
        QUOTENAME(@schemaName) + '.' + QUOTENAME(@objectName) + 
        CASE
            WHEN @defragType = ' REORGANIZE' AND @allowPageLocks = 0 THEN ' REBUILD'
            ELSE ' ' + @defragType
        END

    -- WITH options
    IF @online = 1 OR @sortInTempdb = 1
    BEGIN   
        SET @sql = @sql + ' WITH (' + 
            CASE
                WHEN @online = 1 AND @sortInTempdb = 1 THEN 'ONLINE = ON, SORT_IN_TEMPDB = ON'
                WHEN @online = 1 AND @sortInTempdb = 0 THEN 'ONLINE = ON'
                WHEN @online = 0 AND @sortInTempdb = 1 THEN 'SORT_IN_TEMPDB = ON'
            END + ')'
    END

    IF @partitionCount > 1 AND @disabled = 0 AND @indexType <> 'XML INDEX'
        SET @sql = @sql + ' PARTITION = ' + CAST(@partitionNumber AS varchar(10))

    -- run the ALTER INDEX statement
    EXEC (@SQL)

    -- log some information into a history table
    IF @logHistory = 1
        INSERT INTO DefragmentIndexes (DatabaseName, SchemaName, TableName, IndexName, DefragmentDate, PercentFragmented)
        VALUES(@dbName, @schemaName, @objectName, @indexName, GETDATE(), @fragPercent)

    SELECT @i = MIN(FragIndexId) 
    FROM #FragIndex
    WHERE FragIndexId > @i

    SELECT 
        @objectId = ObjectId, 
        @indexId = IndexId, 
        @fragPercent = FragPercent,
        @partitionNumber = PartitionNumber,
        @indexType = IndexType,
        @allocUnitType = AllocUnitType
    FROM #FragIndex
    WHERE FragIndexId = @i
END

GO

Оригинальный пост здесь:

http://weblogs.sqlteam.com/tarad/archive/2009/08/31/DefragmentingRebuilding-Indexes-in-SQL-server-2005-and-2008.aspx

Помимо перестройки индексов и дефрагментации, единственное, что вы можете сделать, это удалить или избавиться от данных. Если у вас есть int / bigints в качестве PK, это позволит вам затем повторно заполнить свой PK, используя DBCC CHECKIDENT(tablename, value).

Вы можете использовать ALTER INDEX ALL ON MyTable REBUILD для перестройки своих индексов на своей таблице.

2 голосов
/ 13 апреля 2011

Check http://jailer.sourceforge.net/ out. Это инструмент, который может извлекать подмножество данных из БД, сохраняя их согласованность. Я не использовал это сам, но я имел в виду.

2 голосов
/ 12 апреля 2011

Я бы написал скрипт для базы данных, включая таблицы, индексы, триггеры и хранимые процедуры.Затем создайте новую пустую базу данных с помощью этого скрипта.Теперь вы можете добавлять данные в базу данных по мере необходимости для ваших интеграционных тестов.

Вы можете использовать такие инструменты, как http://code.google.com/p/ndbunit/, чтобы загрузить данные для тестов таким образом, чтобы данные были частью теста и будут удалены после его завершения.Также я бы запустил тесты в SQL Express на локальном компьютере разработчиков, чтобы тесты не проваливались, если их одновременно запускают несколько разработчиков.

1 голос
/ 12 апреля 2011

По моему мнению, подмножества "реальных данных" не должны использоваться для юнит-тестов.Модульные тесты должны быть независимы от исходного содержимого базы данных.Они должны создать данные, необходимые для конкретного теста, выполнить тест, а затем удалить данные.В качестве альтернативы весь тест должен находиться в транзакции, откат которой выполняется в конце.

Если вы этого не сделаете, ваши тесты не пройдут, когда кто-то решит удалить или изменить данные, от которых он зависит, ивы потратите огромное количество времени, пытаясь выяснить, почему ваши тесты неожиданно начали проваливаться.

Для системы обеспечения качества или интеграции вы должны иметь возможность создавать подмножество ваших данных на основе ваших знанийотношений между таблицами.

1 голос
/ 12 апреля 2011

Я бы ни при каких обстоятельствах не позволил разработчикам разрабатывать базу данных меньшего размера, когда ей придется работать на одном таком размере.У вас будут проблемы, которые возникают только тогда, когда дела идут вразнос, и это глупая идея.Запросы, которые хорошо работают с небольшими наборами данных, не являются запросами, которые хорошо работают с большими наборами данных.Потеря времени на написание запросов, которые не могут не выполняться в рабочей среде, является одной из причин, по которой глупо разрешать разработчикам работать с небольшим набором данных.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...