SQL Server - 最佳实践 - SSMS配合BCP迁移SQL Server数据库上阿里云

本文讨论的主题是使用SSMS(SQL Server Management Studio)配合BCP命令行的方式来迁移SQL Server数据库。使用SSMS做数据库结构迁移,使用BCP命令做全量数据迁移,此方案是以本地SQL Server数据库迁移到阿里云RDS SQL Server 2012为例。
如果你觉得读取文章不够直观,请点击观看Youku视频,近25分钟的视频详细介绍来如何使用SSMS + BCP迁移SQL Server数据库上阿里云RDS SQL Server。使用SSMS+BCP迁移SQL Server数据库上阿里云RDS SQL Server

背景信息

本方法适用于SQL Server数据库结构迁移和全量数据迁移
本方法仅适用于数据全量迁移,不支持数据增量迁移
本方法适用于本地数据到本地数据库、本地数据库上云到RDS SQL Server数据库、RDS SQL Server数据库到RDS SQL Server数据库三种场景的数据库全量迁移
本文以迁移本地SQL Server 2012数据库AdventureWorks2012到阿里云RDS SQL Server 2012为例,讲述详细的迁移步骤

总体步骤

本文档详细的操作步骤和具体实施过程稍显繁琐,在详细的操作步骤之前,我们梳理下总体的思路和方法。这样可以,从大处着眼,小处着手,条理清楚,思路清晰。我们的目标是SQL Server本地数据库全量迁移或者是本地数据库上云RDS SQL Server,我们必须完成三大任务:
准备工作
对象结构迁移
数据全量迁移

准备工作

准备工作包括源端数据的准备工作和目标数据库的准备工作。
源端数据库:创建用户和断开客户端连接。
目标数据库:创建数据库、确保排序规则一致和创建用户。

对象结构迁移

微软SQL Server Management Studio(简称:SSMS)工具提供了数据库对象结构创建脚本的生成功能。所以,我们直接使用这个功能来生成源端数据库对象结构的创建脚本,然后在目标数据库中去执行。为了不遗漏任何的数据库对象,我们需要对比源端数据库和目标数据库的对象信息,确保一致。具体的步骤可以分解为以下几个小的步骤:
源端数据库获取对象创建脚本
目标数据库执行对象创建脚本
源端数据库和目标数据库获取对象信息
对象信息对比
注意:
这里需要特别提醒,为了避免数据全量迁移过程报错和提高数据导入效率,我们需要在“目标数据库”中先禁用外键、索引和触发器,数据导入完毕后,再启用这三类对象。

数据全量迁移

我们使用BCP命令行来做数据库数据的导出导入功能。为了确保数据全量迁移成功,我们需要对比源端数据库和目标数据库中表记录数是一致的。具体的操作步骤分解为:
BCP从源端数据库导出数据
BCP导入数据到目标数据库
表记录数对比
目标数据库启用外键约束、索引和触发器

准备工作

源端数据库

源端数据库的准备工作包括用户创建和断开客户端连接。

用户创建

在源端数据库创建用户登录,如果已经存在用户,并具备读写该数据库权限,请跳过该步骤。创建用户代码如下:

USE MASTER
GO
CREATE LOGIN testdbo
    WITH PASSWORD = N'XXXXXXXX',CHECK_POLICY = OFF
GO
USE AdventureWorks2012
GO

CREATE USER testdbo FOR LOGIN testdbo;

EXEC sys.sp_addrolemember 'db_owner','testdbo'
GO

断开客户端连接

由于本文讨论的方法仅支持全量迁移,为了确保源端数据库迁移前后的数据是完全一致的,所以在开始迁移之前一定要确保源端数据库数据没有任数据变更操作。我们可以采用断开源端数据库所有的客户端连接的方式来达到这个目的:
停止相关的所有应用程序
停止源端数据库SQL Agent服务
停掉Service Broker端口(如果部署有Service Broker)
当然,你也可以采用其他方法来确保源端数据库没有数据变更操作。

目标数据库

目标数据库的准备工作包括:检查存储空间、创建数据库、确保排序规则一致和创建用户。

检查存储空间

目标数据库主机需要有充足的存储空间来存放导入的数据和因此而带来的日志文件增长,两者加起来的空间增长大概是源端数据库大小的2-3倍(如果是数据库是Full模式)。如果目标数据库是在本地自建环境,请确保宿主机有足够的存储空间,如果是阿里云RDS SQL Server,请确保已经购买了充足的存储空间。

创建数据库

创建数据库的步骤很简单,如果是本地数据库或者阿里云RDS SQL Server 2012,请参见CREATE DATABASE语句。如果是阿里云RDS SQL Server 2008R2,可以使用用户控制台创建新的数据库。这里需要确保新建数据库排序规则与源端数据库保持一致。

确保排序规则和源端数据库一致

我们需要确保目标数据库排序规则和源端数据库排序规则一致,以免数据导入失败。修改排序规则的方法如下:

-- Check Collation name
SELECT name,collation_name
FROM sys.databases
WHERE name = 'adventureworks2012'

-- change the collate if need.
USE master;
GO
ALTER DATABASE adventureworks2012
COLLATE SQL_Latin1_General_CP1_CI_AS;
GO

用户创建

如果是本地环境数据库或者RDS SQL Server 2012,请参考“源端数据库中的用户创建”部分来创建用户;如果是RDS SQL Server 2008R2,请使用用户控制台来创建具有读写权限的用户。

操作步骤

以下是SQL Server数据库结构信息和全量数据迁移的详细实现步骤。

源端数据库获取对象创建脚本

这一步是生成源端数据库对象信息创建脚本,我们使用SSMS自带的脚本生成工具。方法是
展开Databases => 右键点击相应数据库 => 选择Tasks => Generate Scripts。如下图所示:

一般性介绍页面

选择要导出脚本的对象

生成脚本选项设置,这一步是最关键的地方,比如:
Script for Server Version:可以选择生成脚本适用的SQL Server版本(目标数据库版本),这个选项使得不同版本间数据迁移成为可能;
Types of data to script:这里请选择Schema only,否则会生成INSERT插入数据的语句;
Table/View Options:建议全部选择True。

汇总信息,这里关注下生成的脚本文件目录。

最后完成脚本生成工作。

目标数据库创建对象并禁用外键、索引和触发器

在上一步,我们已经将源端数据库所有对象生成了创建的脚本文件,接下来,需要在目标数据库中执行该脚本文件来创建这些对象到目标数据库中。使用SSMS连接目标数据库实例,然后打开我们之前生成的脚本文件,并执行。

目标数据库完成对象创建以后,一个非常重要的步骤是需要禁用表外键约束、索引和触发器。因为,表外键约束的存在会导致数据导入失败,而表索引和触发器的存在会导致数据导入效率降低。为了达到这个目的,请在目标数据库中执行以下脚本。注意输入参数@is_disable = 1表示禁用外键约束、索引和触发器;@is_disable = 0表示启用外键约束、索引和触发器。

USE [adventureworks2012]
GO

--public variables: need init by users.
DECLARE
    @is_disable BIT = 1 -- 1: disalbe indexes, foreign keys and triggers;
                        -- 0: enable indexes, foreign keys and triggers;
;

--================ Private variables
DECLARE
    @sql NVARCHAR(MAX)
    , @sql_index NVARCHAR(MAX)
    , @tb_schema SYSNAME
    , @tb_object_name SYSNAME
    , @tr_schema SYSNAME
    , @tr_object_name SYSNAME
    , @ix_name SYSNAME
;

--================= Disable/Enable indexes on all tables
DECLARE
    cur_indexes CURSOR LOCAL STATIC FORWARD_ONLY READ_ONLY
FOR
SELECT
    ix_name = ix.name
    , tb_schema = SCHEMA_NAME(obj.schema_id)
    , tb_object_name = obj.name
FROM sys.indexes as ix
    INNER JOIN sys.objects as obj
    ON ix.object_id = obj.object_id
WHERE ix.type >= 2
    AND obj.is_ms_shipped = 0
    AND ix.is_disabled = (1 - @is_disable)

OPEN cur_indexes;
FETCH NEXT FROM cur_indexes INTO @ix_name, @tb_schema, @tb_object_name;

WHILE @@FETCH_STATUS = 0
BEGIN
    SET
        @sql_index = N'ALTER INDEX ' + QUOTENAME(@ix_name)
                    + N' ON ' + QUOTENAME(@tb_schema) + N'.' + QUOTENAME(@tb_object_name)
                    + CASE @is_disable
                        WHEN 1 THEN N' DISABLE;'
                        WHEN 0 THEN N' REBUILD; '
                        ELSE N''
                    END;
    RAISERROR(N'%s', 10, 1, @sql_index) WITH NOWAIT;
    EXEC sys.sp_executesql @sql_index
    FETCH NEXT FROM cur_indexes INTO @ix_name, @tb_schema, @tb_object_name;
END

CLOSE cur_indexes;
DEALLOCATE cur_indexes;

--================= Disable/Enable foreign keys on all tables
--disable
IF @is_disable = 1
BEGIN
    SELECT
        @sql = N'
        RAISERROR(N''ALTER TABLE ? NOCHECK CONSTRAINT ALL;'', 10, 1) WITH NOWAIT
        ALTER TABLE ? NOCHECK CONSTRAINT ALL;'
    ;
END
ELSE    --enable
BEGIN
    SELECT
        @sql = N'
        RAISERROR(N''ALTER TABLE ? WITH CHECK CHECK CONSTRAINT ALL;'', 10, 1) WITH NOWAIT
        ALTER TABLE ? WITH CHECK CHECK CONSTRAINT ALL;'
    ;
END

EXEC sys.sp_MSforeachtable @sql

--================= Disable/Enable triggers on all tables

DECLARE
    cur_triggers CURSOR LOCAL STATIC FORWARD_ONLY READ_ONLY
FOR
SELECT
    tb_schema = SCHEMA_NAME(tb.schema_id)
    ,tb_object_name = tb.name
    ,tr_schema = SCHEMA_NAME(obj.schema_id)
    ,tr_object_name = obj.name
FROM sys.objects as obj
    INNER JOIN sys.tables as tb
    ON obj.parent_object_id = tb.object_id
    INNER JOIN sys.triggers as tr
    ON obj.object_id = tr.object_id
WHERE obj.type = 'TR'
    AND obj.is_ms_shipped = 0
    AND tr.is_disabled =  (1 - @is_disable)
ORDER BY tb_schema, tb_object_name

OPEN cur_triggers;
FETCH NEXT FROM cur_triggers INTO @tb_schema, @tb_object_name, @tr_schema, @tr_object_name;

WHILE @@FETCH_STATUS = 0
BEGIN
    SET @sql = CASE @is_disable
                    WHEN 1 THEN N'DISABLE TRIGGER '
                    WHEN 0 THEN N'ENABLE TRIGGER '
                    ELSE N''
                END
                + QUOTENAME(@tr_schema) + N'.' + QUOTENAME(@tr_object_name)
                + N' ON '
                + QUOTENAME(@tb_schema) + N'.' + QUOTENAME(@tb_object_name)
    ;
    RAISERROR(N'%s', 10, 1, @sql) WITH NOWAIT;
    EXEC sys.sp_executesql @sql;
    FETCH NEXT FROM cur_triggers INTO @tb_schema, @tb_object_name, @tr_schema, @tr_object_name;
END

CLOSE cur_triggers;
DEALLOCATE cur_triggers;
GO

源端和目标数据库获取对象信息并比较

对象信息是指数据库下存在的各种对象信息,包含但不仅限于表、视图、函数、触发器、约束和索引等。请在源端和目标数据库中分别执行以下代码,获取数据库的对象汇总信息。

USE AdventureWorks2012
GO
;WITH objs
AS(
-- check objects
SELECT
    database_name = LOWER(DB_NAME())
    , object_type = type
    , objet_desc = type_desc
    , object_count = COUNT(1)
FROM sys.all_objects WITH(NOLOCK)
WHERE is_ms_shipped = 0
GROUP BY type,type_desc
UNION ALL

--check indexes
SELECT
    database_name = LOWER(DB_NAME())
    , object_type = CAST(ix.type AS VARCHAR)
    , objet_desc = ix.type_desc
    , object_count = COUNT(1)
FROM sys.indexes as ix
    INNER JOIN sys.objects as obj
    ON ix.object_id = obj.object_id
WHERE obj.is_ms_shipped = 0
GROUP BY ix.type,ix.type_desc
)
SELECT * FROM objs
ORDER BY object_type

源端和目标数据库对象信息获取完毕后,接下来是比较汇总信息。为了方便对比和避免人眼观察带来的人为错误,我们建议使用对比工具来对比对象汇总信息,推荐使用的是:Araxis Merge 2001 v6.0 Professional这款对比工具。
首先,我们将源端数据库获取到的对象汇总信息复制到对比工具左侧窗口中,复制方法如下图所示:

然后,将目标数据库对象汇总信息复制到对比工具右侧窗口中。
接下来,由于阿里云RDS SQL Server目标数据库名称仅支持小写字母,而源数据库名可能含有大写字母,所以我们需要修改对比工具的设置,忽略字母大小写。方法如下:
View => Options => 选中”Ignore differences in character case”

修改设置之前,对比工具认为左右两边窗口的信息是不一样的,对比工具已经高亮显示了不同的地方;修改设置忽略大小写之后,对比工具显示左右窗口中的信息已经完全一样了。因此,我们可以认为所有的对象信息已经从源数据库迁移到了目标数据库。这是我们在下一步导入数据到目标数据库开始之前一定要确保成功的。

使用BCP做数据全量迁移

上一个步骤,我们已经确保了源端数据库和目标数据库对象信息已经保持一致了。接下来需要将源端数据库中所有表的所有数据导入到目标数据库对应的表中。这个动作我们使用SQL Server自带的BCP命令行工具,方法如下:
SQL 脚本生成BCP OUT和BCP IN

USE AdventureWorks2012
GO

-- declare public variables, need to init by user
DECLARE
    @source_Instance sysname
    , @source_Database sysname
    , @source_User sysname
    , @source_Passwd sysname

    , @destination_Instance sysname
    , @destination_Database sysname
    , @destination_User sysname
    , @destination_Passwd sysname

    , @batch_Size int

    , @transfer_table_list nvarchar(max)
;

-- Public variables init.
SELECT
    @source_Instance = @@SERVERNAME             -- Source Instance Name
    , @source_Database = DB_NAME()                  -- Source Database is current database.
    , @source_User = 'XXX'                          -- Source Instance Connect User Name
    , @source_Passwd = N'XXX'               -- Source Instance User Password

    , @destination_Instance = N'XXXX.sqlserver.rds.aliyuncs.com,3433'   -- Destination Instance Name
    , @destination_Database = N''                       -- Destination Database name: NULL/empty: Keep the same as source db
    , @destination_User = 'XXX'                     -- Destination Instance User Name
    , @destination_Passwd = N'XXX'          -- Destination Instance User Password

    , @transfer_table_list = N''                                --NULL/empty: ALL Tables are needed to be transfered.
    , @batch_Size = 50000                                   -- BCP IN Batch Size, by default, it is 50000. Must between 1 and 50000.
;

-- Private variables, there is no need to init.
DECLARE
    @transfer_table_list_xml xml
    , @timestamp char(14)
    ;

-- correct the variables init by user.
SELECT
    @source_Instance = RTRIM( LTRIM(@source_Instance) )
    , @source_User = RTRIM( LTRIM( @source_User ) )
    , @source_Passwd = RTRIM( LTRIM( @source_Passwd ) )

    , @destination_Instance = RTRIM( LTRIM( @destination_Instance ) )
    , @destination_Database =  CASE
                                                WHEN ISNULL(@destination_Database, N'') = N'' THEN @source_Database
                                                ELSE @destination_Database
                                            END
    , @destination_User = RTRIM( LTRIM( @destination_User ) )
    , @destination_Passwd = RTRIM( LTRIM( @destination_Passwd ) )

    , @batch_Size = CASE
                                WHEN (@batch_Size>0 AND @batch_Size<=50000) THEN @batch_Size
                                ELSE 50000
                            END
    , @transfer_table_list_xml = '<V><![CDATA[' + REPLACE(
                                                    REPLACE(
                                                                REPLACE(
                                                                            @transfer_table_list,CHAR(10),']]></V><V><![CDATA['
                                                                        ),',',']]></V><V><![CDATA['
                                                            ),CHAR(13),']]></V><V><![CDATA['
                                                  ) + ']]></V>'
    , @timestamp =
                    REPLACE(
                        REPLACE(
                                REPLACE(
                                            CONVERT(CHAR(19), GETDATE(), 120), N'-', '')
                                        , N':', N'')
                                    , CHAR(32), N'')
;

IF OBJECT_ID('tempdb..#tb_list', 'U') IS NOT NULL
    DROP TABLE #tb_list
CREATE TABLE #tb_list(
 RowID INT IDENTITY(1, 1) NOT NULL PRIMARY KEY
 ,Table_name SYSNAME NOT NULL
)

IF ISNULL(@transfer_table_list, '') = ''
BEGIN
    INSERT INTO #tb_list
    SELECT name
    FROM sys.tables AS tb
    WHERE tb.is_ms_shipped = 0
END
ELSE
BEGIN
    INSERT INTO #tb_list
    SELECT table_name = T.C.value('(./text())[1]','sysname')
    FROM @transfer_table_list_xml.nodes('./V') AS T(C)
    WHERE T.C.value('(./text())[1]','sysname') IS NOT NULL
END
;

SELECT
    BCP_OUT = N'BCP ' + @source_Database + '.' + sch.name + '.' + tb.name
                    + N' Out '
                    + QUOTENAME( REPLACE(@source_Instance, N'\', N'_')+ '.' + @source_Database + '.' + sch.name + '.' + tb.name, '"')
                    + N' /N /U ' + @source_User +N' /P ' + @source_Passwd +N' /S ' + @source_Instance
                    + N' >> BCPOUT_' + @timestamp +N'.txt'
    ,BCP_IN = N'BCP ' + @destination_Database + '.' + sch.name + '.' + tb.name
                    + N' In '
                    + QUOTENAME( REPLACE(@source_Instance, N'\', N'_')+ '.' + @source_Database + '.' + sch.name + '.' + tb.name, '"')
                    + N' /N /E /q /k /U ' + @destination_User + N' /P ' + @destination_Passwd + N' /b '
                    + CAST(@batch_Size as varchar) + N' /S ' + @destination_Instance
                    + N' >> BCPIN_' + @timestamp + N'.txt'
    --,*
FROM sys.tables as tb
    LEFT JOIN sys.schemas as sch
    ON tb.schema_id = sch.schema_id
WHERE tb.is_ms_shipped = 0
AND tb.name IN (SELECT Table_name FROM #tb_list)

在源端数据库执行上面的脚本
请参照下面截图中的说明修改两个红色框中部分。

保存执行结果中BCP_OUT列所有内容到文件BCPOUT.bat
保存执行结果中BCP_IN列所有内容到文件BCPIN.bat
执行BCPOUT.bat文件
检查BCPOUT.bat执行的日志文件
BCPOUT.bat批处理文件执行后会生成一个日志文件,日志文件的命名格式是:BCPOUT_YYYYMMDDHHMMSS.txt,比如:BCPOUT_20170123113408.txt。
执行BCPIN.bat文件
检查BCPIN.bat执行的日志文件
BCPIN.bat批处理文件执行后会生成一个日志文件,日志文件的命名格式是:BCPIN_YYYYMMDDHHMMSS.txt,比如:BCPIN_20170123113408.txt。
删除BCP导出的中间临时文件
如果确保数据已经从源端数据库导入到目标数据,磁盘上的这些中间临时文件就可以删除了。

表记录数对比

在源端数据库和目的端数据库分别执行下面的语句,统计每个表总共存在的记录总数。然后参照“源端和目标数据库获取对象信息并比较”中对比工具使用方法,对比执行结果,最终来确保源端数据库和目标数据库数据保持一致。

USE AdventureWorks2012
GO
SELECT
    schema_name = SCHEMA_NAME(tb.schema_id)
    ,table_name = OBJECT_NAME(tb.object_id)
    ,row_count = SUM(CASE WHEN ps.index_id < 2 THEN ps.row_count ELSE 0 END)
FROM sys.dm_db_partition_stats as ps WITH(NOLOCK)
    INNER JOIN sys.tables as tb WITH(NOLOCK)
    ON ps.object_id = tb.object_id
WHERE tb.is_ms_shipped = 0
GROUP BY tb.object_id,tb.schema_id
ORDER BY schema_name,table_name

源端数据库和目标数据库所有表记录总数相同,对比结果如下图所示:

目标数据库启用表外键、索引和触发器

在确保目标数据库表的所有数据已经导入完毕以后,最后一个步骤是启动目标数据库中所有表的外键约束、索引和触发器。这个步骤操作非常简单,只需要将“目标数据库创建对象并禁用外键、索引和触发器”中的脚本输入参数设置为@is_disable = 0,执行脚本即可。这个脚本可能会运行比较长的时间,请不要惊慌或者手动终止,因为脚本会做表索引的重建工作,具体运行时间视数据量大小而定,你可以通过Messages窗口查看当前进度。

注意

排序规则的一致性
在目标数据库创建的时候,一定要确保目标数据库排序规则和源端数据库保持一致,否则很可能会导致全量数据迁移失败。
注意外键、索引和触发器
为了防止数据全量迁移过程报错,需要在“目标数据库”中禁用外键、索引和触发器,然后再启用,以此来避免错误发生和提高数据导入效率。
时间戳列和计算列
BCP导出的数据文件中针对计算列或 timestamp 列,BCP导入时这些列值将被忽略,SQL Server 将自动分配该列的值。
如果遭遇错误
因为难免考虑不周,如果您在参照这个过程遇到任何问题或者错误,请联系阿里云,以便我们及时纠正错误并竭诚为您服务。

时间: 2024-10-31 13:57:01

SQL Server - 最佳实践 - SSMS配合BCP迁移SQL Server数据库上阿里云的相关文章

RDS SQL Server - 最佳实践 - 高CPU使用率系列之数据类型转换

摘要 前两篇文章讨论了导致CPU高使用率的两个重要原因是索引缺失和索引碎片,本系列文章之三讨论数据类型隐式转换话题. 场景分析 在SQL Server中,比较运算符(大于.小于.等于或者连接)两端的数据类型需要保持一直才能进行.否则,SQL Server会按照数据类型优先级由低到高进行隐式转化,然后再进行比较.这个行为可以通过执行计划中的CONVERT_IMPLICIT关键字看出来,后面的测试例子中,我们可以清楚的看到这一点.如果很不幸,导致SQL Server正式表字段数据类型隐式转换会带来几

SQL Server - 最佳实践 - 参数嗅探问题

title: SQL Server - 最佳实践 - 参数嗅探问题 author: 风移 摘要 MSSQL Server参数嗅探既是一个涉及知识面非常广泛,又是一个比较难于解决的课题,即使对于数据库老手也是一个比较头痛的问题.这篇文章从参数嗅探是什么,如何产生,表象是什么,会带来哪些问题,如何解决这五个方面来探讨参数嗅探的来龙去脉,期望能够将SQL Server参数嗅探问题理清楚,道明白. 什么参数嗅探 当SQL Server第一次执查询语句或存储过程(或者查询语句与存储过程被强制重新编译)的时

RDS SQL Server - 最佳实践 - 高CPU使用率系列之二索引碎片

摘要 上一篇文章分析了高CPU使用率的原因之一是索引缺失,接下来本系列文章之二的"索引碎片"是CPU高使用率的又一常见的原因.解决索引碎片问题是解决SQL Server服务响应缓慢,查询超时的又一利器. 问题引入 "鸟哥,我上一篇文章分享了因为索引缺失导致CPU高使用率的话题,反响不错.接下来,我打算分享索引碎片导致CPU高使用率的话题.",菜鸟主动找到老鸟汇报工作. 上一篇文章详情参见链接:RDS SQL Server - 最佳实践 - 高CPU使用率系列之索引缺

RDS SQL Server - 最佳实践 - 高CPU使用率系列之索引缺失

摘要 CPU高使用率往往会导致SQL Server服务响应缓慢,查询超时,甚至服务挂起僵死,可以说CPU高使用率是数据库这种后台进程服务的第一大杀手.本系列文章之一的"索引缺失"就是CPU高使用率的最常见的原因之一. 问题引入 "鸟啊,我们平时在服务阿里云RDS SQL Server客户的过程中,遇到最多的一个问题就是,客户反馈RDS SQL Server数据库CPU使用率很高(有时超过90%,甚至到100%),导致查询缓慢甚至超时,这类问题要如何解决啊?".老鸟已

SQL Server · 最佳实践 · 参数嗅探问题

摘要 MSSQL Server参数嗅探既是一个涉及知识面非常广泛,又是一个比较难于解决的课题,即使对于数据库老手也是一个比较头痛的问题.这篇文章从参数嗅探是什么,如何产生,表象是什么,会带来哪些问题,如何解决这五个方面来探讨参数嗅探的来龙去脉,期望能够将SQL Server参数嗅探问题理清楚,道明白. 什么参数嗅探 当SQL Server第一次执行查询语句或存储过程(或者查询语句与存储过程被强制重新编译)的时候,SQL Server会有一个进程来评估传入的参数,并根据传入的参数生成对应的执行计划

RDS SQL Server - 最佳实践 - 高CPU使用率系列之非SARG查询

摘要 阿里云RDS SQL Server客户遇到最多的一个问题便是高CPU使用率导致导致SQL Server服务响应缓慢,查询超时,甚至服务挂起僵死.本系列文章第四篇分析非SARG查询导致CPU的高利用率的解决之道. 问题引入 "鸟啊,你听说过RDBMS的非SARG查询语句吗?我还是今天第一次听说呢!".老鸟有些不解的问菜鸟. "哈哈,鸟哥,孤陋寡闻,土鳖了吧.它可是导致RDBMS数据库CPU高使用率的又一个重要的原因呢!今天就让我细细道来.",菜鸟开始得意忘形起来

PgSQL · 最佳实践 · 从 MaxCompute (ODPS) 迁移数据到 HybridDB

title: PgSQL · 最佳实践 · 从 ODPS 迁移数据到 HybridDB author: 曾文旌(义从) 背景 最近,不少用户在尝试使用 HybridDB 的过程中,询问我们如何把之前在 ODPS 中的数据迁移到 HybridDB.今天就跟大家介绍一种效率较高的方法. 一:原理 ODPS 和 HybridDB 都是多数据节点组合成的集群架构,这样的架构如果要做到效率较高的数据吞吐,需要驱动数据节点主动推送数据.幸运的是 ODPS 和 HybridDB 都支持用该方式向 OSS 读写

贷款、天使投资(风控助手)业务数据库设计 - 阿里云RDS PostgreSQL, HybridDB for PostgreSQL最佳实践

标签 PostgreSQL , HybridDB for PostgreSQL , 小微贷款 , 金融风控 , 企业图谱 , 图式搜索 , 舆情分析 , 自动贷款 , 贷款审查 , 审查神器 背景 贷款是银行的主营业务之一,但是并不是只有银行能提供贷款,实际上资金雄厚的公司都有能力提供贷款(比如保险行业.资源垄断型企业等). 除了放贷,我们常说的天使投资.A轮B轮啥的,也是类似的场景,凭什么投你,背后如何决策也需要决策系统的支撑. 与贷款相反的是吸金类业务,比如我们现在发现越来越多的理财产品.股

(时序业务)证券交易系统数据库设计 - 阿里云RDS PostgreSQL最佳实践

标签 PostgreSQL , 证券 , 时序数据 , JSON , HSTORE , 数组 , range索引 , BRIN块级索引 , 分时走势 , 线性回归 , MADlib , 机器学习 背景 证券行业产生的数据比较多,读写非常频繁. 以股票交易为例,一共有几千只股票.一年大概有240个交易日,交易日大概是从早上10点到下午4点. 1.数据写入需求: 实时的数据写入,按查询维度的实时数据合并(比如秒数据实时写入.分钟,几分钟,...则实时合并). 数据分为不同粒度的分时数据.(精确到秒,