AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / dba / 问题 / 29328
Accepted
DamagedGoods
DamagedGoods
Asked: 2012-11-27 17:10:45 +0800 CST2012-11-27 17:10:45 +0800 CST 2012-11-27 17:10:45 +0800 CST

SQL Server 就地升级是否像以前那样不明智?

  • 772

自 SQL Server 6.5 以来,我一直在使用 SQL Server 和关闭,仍然在我脑海中响起的旧建议是永远不要进行就地升级。

我目前正在将我的 2008 R2 DEV 和 TEST 系统升级到 SQL Server 2012,并且需要使用相同的硬件。不必恢复我的 Reporting Services 配置的想法非常有吸引力,而且我真的很难在时间上明智。不涉及任何分析服务或任何异常或非标准 - 仅安装数据库引擎和报告服务。

有没有人遇到过就地升级的严重问题?还是我应该重新评估我对就地升级的立场?

sql-server sql-server-2012
  • 5 5 个回答
  • 27846 Views

5 个回答

  • Voted
  1. Best Answer
    Mike Walsh
    2012-11-27T17:19:43+08:002012-11-27T17:19:43+08:00

    非常简短的回答- 就地是可以的。之后您可以查看您的配置并实施 SQL Server 2012 的最佳实践。

    SQL Server 升级/迁移的更长答案

    所以这是一个意见问题,不一定有错误或正确的答案,但出于很多原因,我更喜欢迁移风格升级而不是就地升级。话虽如此 - 我的一些客户由于各种原因别无选择,只能进行就地升级,实际上自 SQL Server 2005 以来,就地升级并没有以前那么糟糕。

    为什么我更喜欢迁移而不是就地升级

    • 更容易回滚- 如果出现问题,您可以通过简单地说“我们中止升级。请在我们解决此问题时将连接字符串更改为旧服务器”来回滚。使用原位,您正在修复它,或者您失败了。
    • 刷新硬件- 硬件变化很快。通过就地升级,您很容易陷入 4 年前适合您公司的硬件,但不适用于今天和未来四年的硬件。无论如何,您可能必须在某个时候为新硬件进行迁移。
    • 感觉更好- 当然...这是主观的,但是知道您从新的操作系统安装开始感觉很好今天)这可能会在未来让你头疼。
    • 新操作系统- 如果您今天不是最新最好的操作系统,迁移让您有机会从新的操作系统版本开始。
    • 您可以对其进行测试- 在安装 SQL 并将其与数据库和使用情况混为一谈之前,是否曾经想在一台新机器上获得一组基线?你现在可以这样做了。
    • 有时潜入最佳实践更容易——也许 SQL Server 服务帐户是本地管理员。也许 Builtin Administrators 是 SA 服务器角色。也许之前有些事情已经被破解在一起以使其正常工作。您可以解决所有问题并重新开始。
    • 免费的测试环境和额外的睡眠- 当您启用这个新环境时,拥有一个可以在实际切换日之前工作的环境是一个很大的好处。迁移到新环境意味着您可以在工作时间构建它,远远早于您的实际切换日,并提前以多种方式对其进行测试。您可以在所有应用程序和系统上运行数天的完整回归测试,并在实际执行最后一组还原/附加和切换所有应用程序和访问新环境之前高枕无忧。
    • 您不必一次全部完成- 我遇到的一个非常常见的情况是一个试图整合到几个实例的环境。也许每个版本一个,也许每个“层”和版本一个。根据测试、项目计划和供应商认证的及时性,这些项目中的许多项目对各种应用程序和数据库都有不同的时间表。进行迁移意味着您可以移动那些准备就绪的数据库,当它们准备就绪时,仍然可以处理那些由于某种原因无法移动的数据库的请求。

    请注意,我并不是说您必须将其作为迁移来执行。如果您不打算在预算范围内购买新硬件并且无法为此次升级购买新硬件,则 In-Place 可以正常工作。升级过程中的支持比 6.5 天要好得多,因此您不会让自己处于不利地位。

    如果您确实计划就地进行开发/测试,但想要进行生产迁移,您可以考虑在生产之前至少进行一次迁移。通过这种方式,您可以提前制定清单并处理您没有想到的任何潜在问题。

    附加/分离与备份/恢复迁移

    如果您决定采用迁移方法,还有一个决定您可能仍有争议,那就是您如何将数据库迁移到新环境。您可以将数据库与旧服务器分离并将其附加到新服务器,也可以将其备份并在那里恢复。

    我更喜欢备份/恢复。我听说分离/附加的最大优势是它节省了一些时间。对我来说,备份/恢复胜出有几个原因:

    • 保持旧的可访问- 这允许您在源服务器上仍然拥有可访问的数据库。分离/附加应该做同样的事情,但它需要几个步骤,并且分离/附加存在人为错误的空间,这可能会使这复杂化。
    • 你保证你有一个备份——而不是仅仅从一个分离中获取一个数据库并且可能忘记备份步骤,你已经确保你已经进行了那个备份。
    • 人为错误- 如果您删除了错误的文件、忘记了发送内容的位置或以其他方式弄乱了您的步骤,那么为数据库移动数据和日志文件会冒很大的风险。现在你可以通过复制而不是剪切来缓解这种情况(如果你分离,你应该摆脱剪切和粘贴的习惯),但你仍然可能会搞砸。SQL Server 不再锁定这些文件,而且意外删除文件太容易了,我冒着风险。
    • 它并没有那么慢-备份和复制它需要更多时间,但我愿意为此付出额外的风险并不多。事实上 - 使用完整恢复模型和日志备份,您可以将停机时间缩短到更低的切换时间,如下面的“如何使迁移方法更快”中所述

    如果您决定进行备份/恢复 - 这意味着您的旧源数据库仍将在线。我喜欢在备份后使该数据库脱机。在编写安全、作业、链接服务器、证书、数据库邮件设置和其他实例范围的信息后,有时我会更进一步,使整个 SQL 实例脱机。这避免了在测试期间有人说“一切看起来都很棒!”的问题。一两天后才意识到他们一直在与旧服务器上的旧数据库通信。使这些数据库脱机或整个实例脱机可以防止这些误报和它们造成的混乱。

    如何使迁移方法更快

    对于繁忙的生产环境,您可以通过使用完全恢复模型最大限度地减少从旧环境切换到新环境所需的停机时间,而停机时间很少。基本上 - 通过恢复最新的完整备份、任何差异备份和任何已采取的日志备份指定您要迁移到的环境NORECOVERY- 然后您为最终切换要做的就是恢复尚未恢复的日志备份和您希望恢复的最终日志备份,指定WITH RECOVERY. 通过这种方式,对于大型数据库,通过在停机时间窗口之前支付完整、差异和大多数日志恢复的成本,可以大大减少实际的切换停机时间窗口。感谢陶在评论中指出这一点!

    如何使就地升级更安全

    在选择就地方法时,您可以做一些事情来改善您的体验和结果。

    • 备份- 提前对您的环境中的所有用户和系统数据库进行适当的备份,并确保它们是好的(我很偏执..我实际上会先在某个地方恢复它们才能真正知道它们是好的..可能会浪费你的时间。 . 但是如果发生灾难,您可能会感谢自己).. 编写有关该环境中 SQL 和 OS 安装的任何配置信息。
    • 在开始之前做好测试- 验证您是否拥有良好的环境和良好的数据库。您应该做一些事情,例如查看错误日志并定期运行 DBCC CHECKDB,但在进行就地升级之前是开始的好时机。提前解决任何问题。
    • 确保操作系统健康——不要只是确保 SQL 是健康的,还要确保你的服务器是健康的。您的系统或应用程序错误事件日志中有任何棘手的错误吗?你的空闲空间怎么样?
    • 为最坏的情况做准备——我不久前有一个博客文章系列,其前提是如果你不为失败做准备——你实际上是在准备失败......我仍然相信这一点。因此,请仔细考虑您可能遇到的问题并提前相应地处理它们。让自己处于“失败”的心态中,你会想到否则你不会想到的事情。

    升级或迁移清单的重要性

    如果您决定进行升级(无论是就地升级还是迁移),您应该认真考虑创建一个清单并在每个环境中使用此清单。您应该在此清单中包含很多内容,其中最重要的是:

    1. 开始- 执行一些操作,例如执行测试升级,在最新的数据库兼容性级别上测试您的应用程序,并考虑提前运行SQL Server Upgrade Advisor之类的工具,以查看在执行 SQL 之前需要完成哪些类型的任务服务器升级或迁移。
    2. 预步骤- 清理、操作系统任务、提前打补丁、为升级准备应用程序(干净关闭、连接字符串工作)、备份等。
    3. 升级/迁移步骤- 为使升级或迁移成功并按正确顺序执行的所有操作。安装、更改(或不更改,取决于您的测试和方法)对数据库的兼容性模式更改等。
    4. 迁移/升级后步骤- 各种测试、发布新版本或新服务器配置选项、最佳实践实施、安全更改等。
    5. 回滚步骤- 在整个过程中,您应该有回滚步骤和里程碑。如果你走到这一步并且发生这种情况,你会怎么做?什么是“完全回滚”标准?以及如何进行回滚(反向连接字符串更改、更改设置、返回旧版本、重新安装(如果有)、如果迁移则指向旧服务器等)

    然后让将进行生产升级的人在生产以外的某些环境中遵循清单 - 特别是如果可能的话,关闭类似于生产的环境(“产品以南”,正如我所说......)并注意任何问题或要点由于缺少清单,他们不得不从清单中转移或即兴创作。然后将更改合并,并享受您的生产更改的乐趣。

    我不能过分强调在迁移或升级后和迁移之前进行彻底测试的重要性。在升级过程中做出回滚决定应该很容易——尤其是在迁移期间。如果有什么不舒服的地方,请回滚并找出是否在迁移过程中无法有效且可靠地排除故障。一旦您生活在这个新环境中并且用户连接 - 回滚就成为一项艰巨的任务。您无法将 SQL Server 数据库还原到早期版本。这意味着手动工作和数据迁移。我总是等待几个星期来杀死旧环境,但你应该尽你所能避免需要旧环境,方法是在你的实时用户接触新环境之前找到所有问题。最好在您开始升级/迁移之前。

    关于 SQL Server Reporting Services 迁移/升级 的快速说明 迁移 SSRS 安装并不是许多人认为的艰巨任务。这篇technet/books 在线文章实际上非常方便。该文章中最重要的警告之一是“备份加密密钥”,特别是如果您保存了大量敏感信息,例如预定报告电子邮件收件人电子邮件地址、大量连接的连接信息等。您不久前可以问我的一位客户这有多重要。他们知道,因为我搞砸了这一步,并花了很多时间修改报告计划和连接字符串权限。

    • 95
  2. Ali Razeghi - AWS
    2012-11-27T17:17:07+08:002012-11-27T17:17:07+08:00

    根据我的经验,应该像以前一样做出相同的决策过程。AFAIK 没有任何“世界改变者”与 SQL Server 安装、MS SQL Server 产品本身以及在推出具有数百万行代码的软件时遇到的潜在问题。可能会发生一些不好的事情,现在您无法选择“回滚”选项。

    但是,您确实有其他选择。您可以考虑制作系统快照,在其他地方恢复,执行升级,看看会发生什么。这个测试应该给你很大的安慰,但它并不能绝对保证 prod box 上不会出现任何问题。但是,这是一个在 SQL 6.5 天以前不可用的选项。

    我只是假设最坏的情况。您进行了就地升级,但失败得很惨。然后,您必须在您的 RTO 和 RCO 中从中恢复。企业是否了解风险,您是否制定了减轻风险的计划?

    如果业务对此不满意,那么我的建议是不要这样做。

    • 15
  3. Troy
    2013-12-30T08:29:15+08:002013-12-30T08:29:15+08:00

    如果您的服务器在虚拟环境中运行,您可以对克隆执行快照,然后应用就地升级并测试实例以验证升级是否成功。如果可行,您可以应用快照并使克隆成为生产服务器。如果效果不佳,您可以删除快照并返回升级前的映像重试,或者删除克隆并进行完整迁移。

    • 4
  4. crokusek
    2015-12-18T20:25:49+08:002015-12-18T20:25:49+08:00

    由于大量的硬件投资,我们只需要升级操作系统,同时保持当前的 SQL Server 版本(2012 年,3 台服务器,22 个实例,约 300 个数据库)。没有像镜像等复杂的设置。

    此示例与问题不完全匹配,因为 SQL Server 未升级。我认为这仍然是一个很好的答案,因为显示的步骤实际上比真正的就地迁移更简单。

    概述:连接了外部驱动器以进行完整备份,主要是作为预防措施。只有模型和 msdb 将从外部驱动器实际恢复。ldf/mdf 留在原处以进行分离/附加。数据库中引用了一些本地帐户。在操作系统中重新创建它们之后,数据库中的引用被重新创建(因为 SID 可能会更改)。

    然后是对我们有用的步骤:

    1) 记下将在步骤 12(服务器角色)和 18 到 23 中恢复的服务器级别设置。

    2) 将 SQL Server 2012 修补到 SP3(如果我们要恢复任何系统数据库,则需要一致性)。

    3) 验证每个实例上的版本匹配。“选择@@版本”

    4) 通过运行此脚本生成这 6 个脚本。如果有很多实例,Redgate SQL Multiscript 可以节省大量时间(将工具 -> 选项 => 行长调整为最大值(8192),然后使用文本输出)。

    • 备份
    • 恢复
    • 分离
    • 附
    • 重新创建登录
    • 将用户重新链接到登录

      -- (1) BACKUP / (2) RESTORE
      --    
      --*** SET THESE to external drive location
      --*** and create the Destination Directories
      declare 
          @backupInstanceDir  varchar(300) = 'F:\ExternalDriveBackups\' + replace(@@servername, '\', '_'),
          @dateSuffix         varchar(100) = '2015-12-14'; 
      
      if (object_id('tempdb..DatabaseStatus') is not null)
      drop table #DAtabseSTatus;
      
      select 
          d.name DbName, 
          d.state_desc DbState,
          d.user_access_desc UserMode,
          convert(bit, (d.is_read_only * -1 + 1)) as IsWritable,
          d.is_trustworthy_on as IsTrustWorthy,
          d.is_in_standby IsInStandby,
          d.recovery_model_desc RecoveryModel,
          suser_sname(d.owner_sid) as Owner,
          convert(bit, 
              case when d.database_id <= 4 or d.is_distributor = 1
                  then 1
                  else 0
              end) as IsSystemDb,
          mf.type_desc as FileType,
          mf.name FileName,
          mf.state FileState,
          mf.state_desc FileStatDesc,
          mf.physical_name PhysicalName,
          mf.type as FileTypeId    
      into #DatabaseStatus
      from
          sys.master_files AS mf
      join sys.databases AS d
      ON  mf.database_id = d.database_id
      where
          1=1
      order by
          d.name,
          mf.physical_name;
      
      if object_id('tempdb..#sqlOut') is not null
          drop table #sqlOutBU
      
      if object_id('tempdb..#sqlOut') is not null
          drop table #sqlOutRE
      
      create table #sqlOutBU
      (
          Command nvarchar(max) not null,
          Row int identity(1,1) not null primary key
      );
      
      create table #sqlOutRE
      (
          Command nvarchar(max) not null,
          Row int identity(1,1) not null primary key
      );
      
      insert into #sqlOutBU select char(10) + '-- BACKUP SCRIPT' + char(10);
      insert into #sqlOutRE select char(10) + '-- RESTORE SCRIPT' + char(10);
      
      
      insert into #sqlOutBU select char(10) + char(10) + '/* ---------------------------------------------------------------------------------------------' + char(10) + 
      'ServerName: ' + @@servername + char(10) + 'ServiceName: ' + @@servicename + char(10) + 'Version: ' + @@version + 
      '--------------------------------------------------------------------------------------------- */';
      
      insert into #sqlOutRE select char(10) + char(10) + '/* ---------------------------------------------------------------------------------------------' + char(10) + 
      'ServerName: ' + @@servername + char(10) + 'ServiceName: ' + @@servicename + char(10) + 'Version: ' + @@version + 
      '--------------------------------------------------------------------------------------------- */';        
      
      PRINT '--Script for Backing up all DBs in a SQL Server Instance to a specific location' 
      
      SET nocount ON 
      
      insert into #sqlOutBU select char(10) + 
      '--' + char(10) + '-- BACKUP ' + @@servername + '--' + char(10) + 
      'use [Master]; set deadlock_priority high;' + char(10);
      
      insert into #sqlOutRE select '
      -- RESTORE
      --
      -- BE SURE TO BACKUP SYSTEM DBS TO AN ALTERNATE LOCATION JUST BEFORE RESTORING!
      --
      use [Master]; set deadlock_priority high;' + char(10);
      
      DECLARE @dbname nvarchar(128) 
      declare dblist_cursor cursor fast_forward for 
      select [name] from master.sys.databases where [name] != 'tempdb'
      order by iif(database_id <= 4, '0', '1') + [name]
      
      open dblist_cursor 
      fetch next from dblist_cursor into @dbname 
      
      while @@fetch_status = 0 
      begin 
      
          declare @bak nvarchar(300) = @backupInstanceDir + '\' + @dbname + '_' + @dateSuffix + '.bak';
      
          insert into #sqlOutBU select char(10) + 'backup database [' + @dbname + '] to disk = ''' + @bak + ''' WITH COPY_ONLY, NOFORMAT, NOINIT, ' + char(10) + 
              'NAME = N''' + @dbName + '-Full'', SKIP, NOREWIND, NOUNLOAD, COMPRESSION, STATS = 25;';
      
          insert into #sqlOutRE select 'restore database [' + @dbName + '] from disk = ''' + @bak + ''' WITH FILE = 1,' + char(10) +
          (
              select '    move ''' + FileName + ''' to ''' + PhysicalName + '''' From #DatabaseStatus
              where FileType = 'Rows' and DbName = @dbName
          ) + ',' + char(10) +
          (
              select '    move ''' + FileName + ''' to ''' + PhysicalName + '''' From #DatabaseStatus
              where FileType = 'Log' and DbName = @dbName
          ) + ',' + char(10) +
          '    NOUNLOAD, REPLACE, STATS = 25;' + char(10);               
      
          fetch next from dblist_cursor into @dbname 
      end 
      
      close dblist_cursor 
      deallocate dblist_cursor 
      
      insert into #sqlOutBU select char(10) + 'go' + char(10);
      insert into #sqlOutRE select char(10) + 'go' + char(10);
      
      select Command from #sqlOutBU order by Row; -- BACKUP SCRIPT
      select Command from #sqlOutRE order by Row; -- RESTORE SCRIPT
      
      go
      
      
      
      --
      -- (3) DETACH  -  Org Author: Artemakis Artemiou
      --      
      
      if object_id('tempdb..#sqlOutDT') is not null
          drop table #sqlOutDT
      
      create table #sqlOutDT
      (
          Command nvarchar(max) not null,
          Row int identity(1,1) not null primary key
      );
      
      insert into #sqlOutDT select char(10) + '-- DETACH all DBs from a SQL Server Instance' + char(10);      
      
      insert into #sqlOutDT select char(10) + char(10) + '/* ---------------------------------------------------------------------------------------------' + char(10) + 
      'ServerName: ' + @@servername + char(10) + 'ServiceName: ' + @@servicename + char(10) + 'Version: ' + @@version + 
      '--------------------------------------------------------------------------------------------- */';
      
      SET nocount ON 
      
      insert into #sqlOutDT select char(10) + '--' + char(10) + '-- DETACH ' + @@servername + char(10) + '--' + char(10) + '
      use MAster; set deadlock_priority high;' + char(10) + char(10);
      
      DECLARE @dbname nvarchar(128) 
      DECLARE dblist_cursor CURSOR fast_forward FOR 
      SELECT [name] 
      FROM   master.sys.databases 
      WHERE  database_id > 4 
      
      OPEN dblist_cursor 
      FETCH next FROM dblist_cursor INTO @dbname 
      
      WHILE @@FETCH_STATUS = 0 
      BEGIN 
          insert into #sqlOutDT select
          'alter database ' + @dbname + ' set single_user with rollback immediate;' + char(10) +
          'EXEC sp_detach_db ''' + @dbname + ''', ''true'';' + char(10);
          FETCH next FROM dblist_cursor INTO @dbname 
      END 
      
      CLOSE dblist_cursor 
      DEALLOCATE dblist_cursor 
      
      insert into #sqlOutDT select char(10) + 'go' + char(10);
      select Command from #sqlOutDT order by Row;
      
      go
      
      
      
      --
      -- (4) ATTACH  -  Org Author: Artemakis Artemiou
      --    
      
      if object_id('tempdb..#sqlOut') is not null
          drop table #sqlOutAT
      
      create table #sqlOutAT
      (
          Command nvarchar(max) not null,
          Row int identity(1,1) not null primary key
      );
      
      insert into #sqlOutAT select char(10) + '-- ATTACH ALL DBs to a SQL Server Instance' + char(10);
      
      insert into #sqlOutAT select char(10) + char(10) + '/* ---------------------------------------------------------------------------------------------' + char(10) + 
      'ServerName: ' + @@servername + char(10) + 'ServiceName: ' + @@servicename + char(10) + 'Version: ' + @@version + 
      '--------------------------------------------------------------------------------------------- */';
      
      SET NOCOUNT ON
      
      insert into #sqlOutAT select char(10) + '--' + char(10) + '-- ATTACH ' + @@servername + char(10) + '--' + char(10) + 
      'use MAster;' + char(10) + char(10);
      
      DECLARE @dbname nvarchar(128);
      
      DECLARE DBList_cursor CURSOR fast_forward FOR 
      select [name] from master.sys.databases where database_id > 4
      order by name;
      
      OPEN DBList_cursor
      
      FETCH NEXT FROM DBList_cursor 
      INTO @dbname
      
      WHILE @@FETCH_STATUS = 0
      BEGIN
      
      declare @attach_TSQL_script varchar(max)
      set @attach_TSQL_script=''
      set @attach_TSQL_script=@attach_TSQL_script+'CREATE DATABASE ' + @dbname +' ON ' 
      
      declare @tsql varchar(max),@filename varchar(max)
      set @tsql='DECLARE DBFiles_cursor CURSOR FOR select [filename] from '+ @dbname + '.sys.sysfiles'
      
      execute (@tsql) 
      
      PRINT '--'+@dbname 
      
      OPEN DBFiles_cursor
      FETCH NEXT FROM DBFiles_cursor INTO @filename
      
      WHILE @@FETCH_STATUS = 0
      BEGIN   
      set @attach_TSQL_script=@attach_TSQL_script+ char(10)+'    (FILENAME = '''+ @filename +'''),' 
      FETCH NEXT FROM DBFiles_cursor INTO @filename
      END
      
      set @attach_TSQL_script=SUBSTRING(@attach_TSQL_script,0,len(@attach_TSQL_script))
      set @attach_TSQL_script=@attach_TSQL_script+ char(10) +'    FOR ATTACH;';
      
      insert into #sqlOutAT select @attach_TSQL_script + char(10);
      
      PRINT @attach_TSQL_script 
      PRINT ''
      
      CLOSE DBFiles_cursor
      DEALLOCATE DBFiles_cursor
      
      FETCH NEXT FROM DBList_cursor 
      INTO @dbname
      
      END 
      
      CLOSE DBList_cursor
      DEALLOCATE DBList_cursor
      
      insert into #sqlOutAT select char(10) + 'go' + char(10);
      select Command from #sqlOutAT order by Row;
      go
      
      
      
      --
      -- (5) GENERATE A 'RE-CREATE LOGINS' SCRIPT
      --
      -- This script was modified from a version that was designed to copy from one server to another:
      --      http://stackoverflow.com/a/5983773/538763
      --
      
      
      USE [master]
      
      if object_id('tempdb..#sqlOut') is not null
      drop table #sqlOut;
      
      create table #sqlOut
      (
      Command nvarchar(max) not null,
      Row int identity(1,1) not null primary key
      );
      
      insert into #sqlOut select char(10) + '-- RECREATE LOGINS' + char(10);
      
      
      insert into #sqlOut select char(10) + char(10) + '/* ---------------------------------------------------------------------------------------------' + char(10) + 
      'ServerName: ' + @@servername + char(10) + 'ServiceName: ' + @@servicename + char(10) + 'Version: ' + @@version + 
      '--------------------------------------------------------------------------------------------- */';
      
      insert into #sqlOut select 'use Master;' + char(10);
      go
      SET ANSI_NULLS ON
      GO
      SET QUOTED_IDENTIFIER ON
      GO
      declare @Debug bit = 0;
      declare @PartnerServer varchar(100) = @@SERVICENAME;  -- use current server before it is shutdown (disabled below)
      
      declare
          @MaxID int,
          @CurrID int,
          @SQL nvarchar(max),
          @LoginName sysname,
          @IsDisabled int,
          @Type char(1),
          @SID varbinary(85),
          @SIDString nvarchar(100),
          @PasswordHash varbinary(256),
          @PasswordHashString nvarchar(300),
          @RoleName sysname,
          @Machine sysname,
          @PermState nvarchar(60),
          @PermName sysname,
          @Class tinyint,
          @MajorID int,
          @ErrNumber int,
          @ErrSeverity int,
          @ErrState int,
          @ErrProcedure sysname,
          @ErrLine int,
          @ErrMsg nvarchar(2048);
      
      declare @Logins Table (LoginID int identity(1, 1) not null primary key,
                          [Name] sysname not null,
                          [SID] varbinary(85) not null,
                          IsDisabled int not null,
                          [Type] char(1) not null,
                          PasswordHash varbinary(256) null)
      declare @Roles Table (RoleID int identity(1, 1) not null primary key,
                      RoleName sysname not null,
                      LoginName sysname not null)
      declare @Perms Table (PermID int identity(1, 1) not null primary key,
                      LoginName sysname not null,
                      PermState nvarchar(60) not null,
                      PermName sysname not null,
                      Class tinyint not null,
                      ClassDesc nvarchar(60) not null,
                      MajorID int not null,
                      SubLoginName sysname null,
                      SubEndPointName sysname null)
      
      Set NoCount On;
      
      If CharIndex('\', @PartnerServer) > 0
      Begin
      Set @Machine = LEFT(@PartnerServer, CharIndex('\', @PartnerServer) - 1);
      End
      Else
      Begin
      Set @Machine = @PartnerServer;
      End
      
      -- Get all Windows logins from principal server
      Set @SQL = 'Select P.name, P.sid, P.is_disabled, P.type, L.password_hash' + CHAR(10) +
          'From ' /*+ QUOTENAME(@PartnerServer) + '.*/ + 'master.sys.server_principals P' + CHAR(10) +
          'Left Join '/* + QUOTENAME(@PartnerServer) + '.*/ + 'master.sys.sql_logins L On L.principal_id = P.principal_id' + CHAR(10) +
          'Where P.type In (''U'', ''G'', ''S'')' + CHAR(10) +
          'And P.name <> ''sa''' + CHAR(10) +
          'And P.name Not Like ''##%''' + CHAR(10) +
          'and P.Name Not like ''NT SERVICE%''' + CHAR(10) +
          'And CharIndex(''' + @Machine + '\'', P.name) = 0;';
      
      Insert Into @Logins (Name, SID, IsDisabled, Type, PasswordHash)
      Exec sp_executesql @SQL;
      
      -- Get all roles from principal server
      Set @SQL = 'Select RoleP.name, LoginP.name' + CHAR(10) +
          'From '/* + QUOTENAME(@PartnerServer) + '.*/ + 'master.sys.server_role_members RM' + CHAR(10) +
          'Inner Join '/* + QUOTENAME(@PartnerServer) + .*/ +'master.sys.server_principals RoleP' +
          CHAR(10) + char(9) + 'On RoleP.principal_id = RM.role_principal_id' + CHAR(10) +
          'Inner Join '/* + QUOTENAME(@PartnerServer) + '.*/ + 'master.sys.server_principals LoginP' +
          CHAR(10) + char(9) + 'On LoginP.principal_id = RM.member_principal_id' + CHAR(10) +
          'Where LoginP.type In (''U'', ''G'', ''S'')' + CHAR(10) +
          'And LoginP.name <> ''sa''' + CHAR(10) +
          'And LoginP.name Not Like ''##%''' + CHAR(10) +
          'And LoginP.name Not Like ''NT SERVICE%''' + CHAR(10) +
          'And RoleP.type = ''R''' + CHAR(10) +
          'And CharIndex(''' + @Machine + '\'', LoginP.name) = 0;';
      
      Insert Into @Roles (RoleName, LoginName)
      Exec sp_executesql @SQL;
      
      -- Get all explicitly granted permissions
      Set @SQL = 'Select P.name Collate database_default,' + CHAR(10) +
          '   SP.state_desc, SP.permission_name, SP.class, SP.class_desc, SP.major_id,' + CHAR(10) +
          '   SubP.name Collate database_default,' + CHAR(10) +
          '   SubEP.name Collate database_default' + CHAR(10) +
          'From '/* + QUOTENAME(@PartnerServer) + '.*/ + ' master.sys.server_principals P' + CHAR(10) +
          'Inner Join '/* + QUOTENAME(@PartnerServer) + '.*/ + ' master.sys.server_permissions SP' + CHAR(10) +
          CHAR(9) + 'On SP.grantee_principal_id = P.principal_id' + CHAR(10) +
          'Left Join '/* + QUOTENAME(@PartnerServer) + '.*/ + ' master.sys.server_principals SubP' + CHAR(10) +
          CHAR(9) + 'On SubP.principal_id = SP.major_id And SP.class = 101' + CHAR(10) +
          'Left Join '/* + QUOTENAME(@PartnerServer) + '.*/ + ' master.sys.endpoints SubEP' + CHAR(10) +
          CHAR(9) + 'On SubEP.endpoint_id = SP.major_id And SP.class = 105' + CHAR(10) +
          'Where P.type In (''U'', ''G'', ''S'')' + CHAR(10) +
          'And P.name <> ''sa''' + CHAR(10) +
          'And P.name Not Like ''##%''' + CHAR(10) +
          'And P.name Not Like ''NT SERVICE%''' + CHAR(10) +
          'And CharIndex(''' + @Machine + '\'', P.name) = 0;'
      
      Insert Into @Perms (LoginName, PermState, PermName, Class, ClassDesc, MajorID, SubLoginName, SubEndPointName)
      Exec sp_executesql @SQL;
      
      --select * from @Logins;
      --select * from @Roles;
      --select * from @perms;
      
      
      Select @MaxID = Max(LoginID), @CurrID = 1
      From @Logins;
      
      While @CurrID <= @MaxID
      Begin
      Select @LoginName = Name,
          @IsDisabled = IsDisabled,
          @Type = [Type],
          @SID = [SID],
          @PasswordHash = PasswordHash
      From @Logins
      Where LoginID = @CurrID;
      
      --    If Not Exists (Select 1 From sys.server_principals
      --              Where name = @LoginName)
      Begin
      
          set @sql = char(10);
          set @sql += 'If Not Exists (Select 1 From sys.server_principals Where name = ''' + @LoginName + ''')' + char(10);
          set @sql += 'begin' + char(10) + '    ';
      
          Set @SQL += 'Create Login ' + quotename(@LoginName)
          If @Type In ('U', 'G')
          Begin
              Set @SQL = @SQL + ' From Windows;'
          End
          Else
          Begin
              Set @PasswordHashString = '0x' +
                  Cast('' As XML).value('xs:hexBinary(sql:variable("@PasswordHash"))', 'nvarchar(300)');
      
              Set @SQL = @SQL + ' With Password = ' + @PasswordHashString + ' HASHED;  --, ';
      
              Set @SIDString = '0x' +
                  Cast('' As XML).value('xs:hexBinary(sql:variable("@SID"))', 'nvarchar(100)');
              Set @SQL = @SQL + 'SID = ' + @SIDString + ';' + char(10);
          End
      
          set @sql += char(10) +
              '    print ''Created Login ' + @loginName  + ''';' + char(10) +
              'end' + char(10) +
              'else' + char(10) +
              convert(nvarchar(max), '    print ''Login ' + @loginName + ' already existed. '';') + char(10);
      
          If @Debug = 0
          insert into #sqlOut select @SQL;                      
          Else
          Print @SQL;
      
          If @IsDisabled = 1
          Begin
              Set @SQL = 'Alter Login ' + quotename(@LoginName) + ' Disable;'
              If @Debug = 0
                  insert into #sqlOut select @SQL;                              
              Else              
                  Print @SQL;              
          End
          End
      Set @CurrID = @CurrID + 1;
      End
      
      
      insert into #sqlOut select char(10) + 'use Master;' + char(10);
      
      Select @MaxID = Max(RoleID), @CurrID = 1
      From @Roles;
      
      While @CurrID <= @MaxID
      Begin
      Select @LoginName = LoginName,
          @RoleName = RoleName
      From @Roles
      Where RoleID = @CurrID;
      
      /*  If Not Exists (Select 1 From sys.server_role_members RM
                  Inner Join sys.server_principals RoleP
                      On RoleP.principal_id = RM.role_principal_id
                  Inner Join sys.server_principals LoginP
                      On LoginP.principal_id = RM.member_principal_id
                  Where LoginP.type In ('U', 'G', 'S')
                  And RoleP.type = 'R'
                  And RoleP.name = @RoleName
                  And LoginP.name = @LoginName)*/
      Begin
          If @Debug = 0
          Begin          
              insert into #sqlOut select 'Exec sp_addsrvrolemember @rolename = ''' + @RoleName + ''', @loginame = ''' + @LoginName + ''';';
          End
          Else
          Begin
              Print 'Exec sp_addsrvrolemember @rolename = ''' + @RoleName + ''',';
              Print '     @loginame = ''' + @LoginName + ''';';
          End
      End
      
      Set @CurrID = @CurrID + 1;
      End
      
      
      insert into #sqlOut select char(10) + 'use Master;' + char(10);
      
      
      Select @MaxID = Max(PermID), @CurrID = 1
      From @Perms;
      
      While @CurrID <= @MaxID
      Begin
      Select @PermState = PermState,
          @PermName = PermName,
          @Class = Class,
          @LoginName = LoginName,
          @MajorID = MajorID,
          @SQL = PermState + space(1) + PermName + SPACE(1) +
              Case Class When 101 Then 'On Login::' + QUOTENAME(SubLoginName)
                      When 105 Then 'On ' + ClassDesc + '::' + QUOTENAME(SubEndPointName)
                      Else '' End +
              ' To ' + QUOTENAME(LoginName) + ';'
      From @Perms
      Where PermID = @CurrID;
      
      /*If Not Exists (Select 1 From sys.server_principals P
                  Inner Join sys.server_permissions SP On SP.grantee_principal_id = P.principal_id
                  Where SP.state_desc = @PermState
                  And SP.permission_name = @PermName
                  And SP.class = @Class
                  And P.name = @LoginName
                  And SP.major_id = @MajorID)*/
      Begin
          If @Debug = 0
                  insert into #sqlOut select @sql;                      
          Else          
              Print @SQL;          
      End
      
      Set @CurrID = @CurrID + 1;
      End
      
      
      select Command from #sqlOut as SqlOut order by Row;
      go
      
      
      --
      -- (6) Generate a script to Re-link all users to logins based on current state (before shutdown)
      --
      
      use Master;
      
      if object_id('tempdb..#sqlOut') is not null
      drop table #sqlOut;
      
      create table #sqlOut
      (
          Command nvarchar(max) not null,
          Row int identity(1,1) not null primary key
      );
      
      insert into #sqlOut select char(10) + '-- RELINK USERS TO LOGINS' + char(10);
      
      insert into #sqlOut select char(10) + char(10) + '/* ---------------------------------------------------------------------------------------------' + char(10) + 
      'ServerName: ' + @@servername + char(10) + 'ServiceName: ' + @@servicename + char(10) + 'Version: ' + @@version + 
      '--------------------------------------------------------------------------------------------- */';
      
      declare @dbCmd varchar(8000) = '
      use ?;
      
      insert into #sqlOut select char(10) + ''use ?;'' + char(10);  
      
      with links as
      (
      select u.name as UserName,
          l.loginname as LoginName
          from sysusers u 
          join master..syslogins l
          on u.sid = l.sid        
      where u.name != ''dbo''
          and u.isSqlUser = 1 or l.isNtName = 1 or l.isNtGroup = 1
      )
      insert into #sqlOut 
      select ''alter user ['' + UserName + ''] with name = ['' + UserName + ''], login = ['' + LoginName + '']''
      from links
      ';    
      
      exec sp_MSforeachdb @dbCmd;
      
      select Command from #sqlOut order by Row;
      
      go
      

    5) 运行脚本将包括系统(master、msdb、model)在内的所有数据库备份到外部驱动器。

    6)运行脚本以分离所有数据库

    7) C 盘将被重新格式化。如果 LDF/MDF 不在 C 上,请保留它们。

    8) Windows Server 2012 安装在 C 上

    9) 如果原始系统文件不在 C 盘上,请将 LDF/MDF 移开。

    10) 将重新安装 SQL Server 2012 并修补到 SP3 a。重新创建系统用户/组帐户

    11) 将系统数据库备份到新位置或文件名(注意不要覆盖原件!)。

    12) Run recreate roles snippet. Something like:

    USE [master]
    CREATE SERVER ROLE [SomeServerRole]
    --ALTER SERVER ROLE [dbcreator] ADD MEMBER [SomeServerRole]
    --ALTER SERVER ROLE [bulkadmin] ADD MEMBER [SomeServerRole]
    -- ALTER SERVER ROLE [SomeServerRole] ADD MEMBER [SomeMemberOrRole]
    

    13) Run recreate login script (doesn't do anything if logins were restored)

    14) Stop SQL AGENT.

    (Could restore Master here, we chickened out).

    15) Attach mdf/ldf using script from above. a. If fail manually restore from bak using script from above.

    16) Attempt Restore of Model

    17) Ensure SQL Agent is stopped. Restore MSDB (link) a. If fails, need to re-create jobs + maintenance plan + mail configuration + operators

    18) Open User To Login script...

        a. If there are master users (rare?) then First Re-Create users for master since it was not restored:
            use master;       
            CREATE USER [ABC] FOR LOGIN [machine\ABC]
    
        b. Run the rest of the script
    

    19) Enable service broker to match original value SELECT name, is_broker_enabled FROM sys.databases;

        alter database MSDB set single_user with rollback immediate;
        ALTER DATABASE [MSDB] SET ENABLE_BROKER;
        alter database MSDB set multi_user;
    

    20) Start SQL Agent

    21) Set Parallelism threshold to original value

    22) Adjust any database settings to their original values:

     declare @dbCmd varchar(8000) = '
          use ?;
          if db_name() not in (''master'', ''model'', ''tempdb'', ''msdb'')
          begin
                 print ''Adjusting [?]...'';    
                alter database [?] set single_user with rollback immediate;
                 aLTER AUTHORIZATION ON DATABASE::[?] to [sa];
                -- alter database [?] set trustworthy on;
                ALTER DATABASE [?] SET AUTO_CLOSE OFF WITH NO_WAIT;     
                alter database [?] set multi_user;
          end     
          else
                 print ''Skipping [?]...'';
        ';    
    
        exec sp_MSforeachdb @dbCmd;
    

    23) Check job ownership:

    select s.name as JobName, l.name as login, SUSER_SNAME(s.owner_sid) AS login2
    from  msdb..sysjobs s 
    left join master.sys.syslogins l on s.owner_sid = l.sid
    

    If the SQL Server Version had also been upgraded I don't believe the model and msdb databases could have been restored so jobs would have been lost due to https://support.microsoft.com/en-us/kb/264474

    What's missing:

    • Orignal users in master database (rare?)
    • Server Roles
    • ?
    • 2
  5. RowlandG
    2014-06-03T12:26:49+08:002014-06-03T12:26:49+08:00

    这两种方法本身都没有问题——我都做过,而且结果通常都很好。

    如果迁移方法有问题,那不是技术问题:是懒惰。我经常发现一家公司还没有完全迁移到 xxxx 版本的原因是因为他们选择了一次摇摆迁移,而从未有时间做艰苦的工作来完全迁移。现在他们有两套或更多套服务器而不是一套。

    • 1

相关问题

  • SQL Server - 使用聚集索引时如何存储数据页

  • 我需要为每种类型的查询使用单独的索引,还是一个多列索引可以工作?

  • 什么时候应该使用唯一约束而不是唯一索引?

  • 死锁的主要原因是什么,可以预防吗?

  • 如何确定是否需要或需要索引

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    如何查看 Oracle 中的数据库列表?

    • 8 个回答
  • Marko Smith

    mysql innodb_buffer_pool_size 应该有多大?

    • 4 个回答
  • Marko Smith

    列出指定表的所有列

    • 5 个回答
  • Marko Smith

    从 .frm 和 .ibd 文件恢复表?

    • 10 个回答
  • Marko Smith

    如何在不修改我自己的 tnsnames.ora 的情况下使用 sqlplus 连接到位于另一台主机上的 Oracle 数据库

    • 4 个回答
  • Marko Smith

    你如何mysqldump特定的表?

    • 4 个回答
  • Marko Smith

    如何选择每组的第一行?

    • 6 个回答
  • Marko Smith

    使用 psql 列出数据库权限

    • 10 个回答
  • Marko Smith

    如何从 PostgreSQL 中的选择查询中将值插入表中?

    • 4 个回答
  • Marko Smith

    如何使用 psql 列出所有数据库和表?

    • 7 个回答
  • Martin Hope
    Mike Walsh 为什么事务日志不断增长或空间不足? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland 列出指定表的所有列 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney MySQL 能否合理地对数十亿行执行查询? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx 如何监控大型 .sql 文件的导入进度? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison 你如何mysqldump特定的表? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    pedrosanta 使用 psql 列出数据库权限 2011-08-04 11:01:21 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 对 SQL 查询进行计时? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas 如何从 PostgreSQL 中的选择查询中将值插入表中? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 列出所有数据库和表? 2011-02-18 00:45:49 +0800 CST
  • Martin Hope
    bernd_k 什么时候应该使用唯一约束而不是唯一索引? 2011-01-05 02:32:27 +0800 CST

热门标签

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve