200 ? "200px" : this.width)!important;}
-->

介绍

因为线上alwayson环境的一个数据库上使用内存表。经过大概一个星期监控程序发现了一个非常严重问题这个数据库的日志文件不会截断,已用空间一直在增加(存在定时的每个小时的日志备份),同时内存表数据库文件也无法删除,下面就介绍一下后面我的处理过程。

数据库:SQL Server2014 Enterprise Edition (64-bit)

删除文件


使用一个单独非alwayson环境的数据库测试。

一、创建内存表

  1. ---创建内存表文件组
  2. ALTER DATABASE [test] ADD FILEGROUP [test_ag] CONTAINS MEMORY_OPTIMIZED_DATA
  3. GO
  4. ----创建内存表数据库文件
  5. ALTER DATABASE [test]
  6. ADD FILE
  7. (
  8. NAME = 'test_memory',
  9. FILENAME ='D:\database\memory'
  10. )
  11. TO FILEGROUP [test_ag];
  12. GO

二、删除内存表数据库文件

  1. USE [test]
  2. GO
  3. ALTER DATABASE [test] REMOVE FILE [test_memory]
  4. GO

备注:此时还未创建表,创建完后数据库文件执行删除就无法删除,接下来试试在线文档的删除方法方法

 三、官方相关的删除方法

即使已使用“DBCC SHRINKFILE”操作清空 FILESTREAM 容器,但出于各种系统维护原因,数据库可能仍然需要保留对已删除文件的引用。 sp_filestream_force_garbage_collection (TRANSACT-SQL)将运行 FILESTREAM 垃圾回收器删除这些文件时,则可以安全进行这些操作。 除非 FILESTREAM 垃圾回收器已从 FILESTREAM 容器中删除所有文件,否则 ALTER DATABASEREMOVE FILE 操作将无法删除 FILESTREAM 容器并返回错误。 建议使用以下过程删除 FILESTREAM 容器。

1.运行DBCC SHRINKFILE (TRANSACT-SQL)带有 EMPTYFILE 选项以将此容器的活动内容移动到其他容器。

  1. USE test;
  2. GO
  3. -- Create a data file and assume it contains data.
  4. ALTER DATABASE test
  5. ADD FILE (
  6. NAME = Test1data,
  7. FILENAME = 'D:\database\t1data.ndf',
  8. SIZE = 5MB
  9. );
  10. GO
  11. -- Empty the data file.
  12. DBCC SHRINKFILE (test_memory, EMPTYFILE);
  13. GO

2.确保已在 FULL 或 BULK_LOGGED 恢复模型中执行日志备份。

3.确保复制日志读取器作业已运行(如果相关)。

通过log_reuse_wait_desc的状态可以看到当前数据库已经无需日志备份,当然我已经执行过日志备份。

4.运行sp_filestream_force_garbage_collection (TRANSACT-SQL)强制垃圾回收器删除不再需要此容器中的任何文件。

  1. USE [test]
  2. GO
  3. EXEC sp_filestream_force_garbage_collection @dbname = N'test' @filename = N' test_memory ';

5.执行带有 REMOVE FILE 选项的 ALTER DATABASE,以删除此容器。

  1. USE [test]
  2. GO
  3. ALTER DATABASE [test] REMOVE FILE [test_memory]
  4. GO

还是无法删除!!!

四、问题分析

一开始是在alwayson的环境中删除,提示由于副本的原因无法删除。后面单独在一个非alwayson的环境下的数据库测试同样是无法删除,起初以为是创建了内存表的原因后面测试仅仅创建文件组和文件然后来删除文件同样是无法删除,个人猜测有可能是buffer的缘故;在buffer中一直存在内存表相关的文件存在,通过执行DBCC DROPCLEANBUFFERS命令也无法清空buffer中的内存表对象。使尽浑身解数还是无法将它删除掉,最后只能投降了!!!线上环境等不下去;只能使用最不愿使用的生成表结构导出数据的办法来重建新的数据库。

生成脚本重建数据库


创建一个新的数据库同时保证当前数据库可用(重命名当前的数据库,新创建的数据库使用之前的名称这样可以保证应用程序那边不需要改变),这样如果出现什么问题也可以及时的切换回来。

步骤如下(在允许停机维护的情况下进行):

1.禁用所有相关作业

2禁用应用程序登入用户

同时保证相关进程事务都已完成。

  1. ALTER LOGIN [test] DISABLE
  2. GO
  3.  
  4. USE [master]
  5. GO
  6. ALTER DATABASE [test] SET SINGLE_USER WITH ROLLBACK IMMEDIATE;--将数据库设置成单用户并回滚当前连接
  7.  
  8. USE [test];---保持连接操作,防止其它用户此时进行连接
  9. GO

3.执行checkpoint刷新所有脏页

  1. CHECKPOINT
  2.  
  3. ---返回当前buffer中每个数据库所占的buffer大小和buffer中脏页的大小
  4. WITH CTE1
  5. AS ( SELECT COUNT(*) * 8 / 1024 AS dirty_cached_size_MB ,
  6. COUNT(*) AS dirty_pages,
  7. CASE database_id
  8. WHEN 32767 THEN 'ResourceDb'
  9. ELSE DB_NAME(database_id)
  10. END AS database_name
  11. FROM sys.dm_os_buffer_descriptors
  12. WHERE is_modified = 1
  13. GROUP BY DB_NAME(database_id),database_id
  14. ),
  15. CET2
  16. AS ( SELECT COUNT(*) * 8 / 1024 AS cached_size_MB ,
  17. COUNT(*) AS pages,
  18. CASE database_id
  19. WHEN 32767 THEN 'ResourceDb'
  20. ELSE DB_NAME(database_id)
  21. END AS database_name
  22. FROM sys.dm_os_buffer_descriptors
  23. GROUP BY DB_NAME(database_id),database_id
  24. )
  25. SELECT
  26. CET2.database_name,
  27. CET2.cached_size_MB,
  28. --CET2.pages,
  29. CTE1.dirty_cached_size_MB
  30. --CTE1.dirty_pages
  31. FROM CTE1 INNER JOIN CET2 ON CTE1.database_name = CET2.database_name
  32.  
  33. ---将数据库选项改成多用户访问
  34. ALTER DATABASE [test]
  35. SET MULTI_USER;

4.生成数据库脚本

5.重命名旧的数据库

注意:如果数据库是在alwayson中,需要先从可用性数据库中删除,否则无法重命名数据库。

  1. /*
  2. 1.断开数据库所有连接同时禁止新的连接进来
  3. 2.比如禁止登入用户、将实例设为单用户模式等。
  4. */
  5. ----1.设置数据库脱机
  6. USE [master]
  7. ALTER DATABASE [test] SET OFFLINE WITH ROLLBACK IMMEDIATE;
  8.  
  9. ----2.手动修改数据库物理文件名,例如将test.mdf改成test_old.mdf
  10.  
  11. ----3.语句修改
  12. USE [master]
  13. ALTER DATABASE [test]
  14. MODIFY FILE (NAME = test, FILENAME = 'C:\Program Files\Microsoft SQL Server\MSSQL12.MSSQLSERVER\MSSQL\DATA\test_old.mdf');
  15. GO
  16. ALTER DATABASE [test]
  17. MODIFY FILE (NAME = test_log, FILENAME = 'C:\Program Files\Microsoft SQL Server\MSSQL12.MSSQLSERVER\MSSQL\DATA\test_old_log.ldf');
  18. GO
  19.  
  20. ---4.设置数据库在线
  21. USE [master]
  22. ALTER DATABASE [test] SET ONLINE
  23.  
  24. ----5.修改数据库逻辑文件名
  25.  
  26. USE [test]
  27. GO
  28. ALTER DATABASE [test] MODIFY FILE (NAME=N'test', NEWNAME=N'test_old')
  29. GO
  30. USE [test]
  31. GO
  32. ALTER DATABASE [test] MODIFY FILE (NAME=N'test_log', NEWNAME=N'test_old_log')
  33. GO
  34.  
  35. ----6.重命名数据库
  36. USE [master]
  37. EXEC sp_renamedb N'test', N'test_old';
  38.  
  39. ----7.查询
  40. SELECT *
  41. FROM sys.master_files
  42. WHERE database_id = DB_ID('test_old');

6.创建新的数据库同时导入脚本到新的数据库

如果同时导出表结构和数据在ssms工具中执行可能会因为脚本过大无法执行,可以使用sqlcmd工具执行脚本导入,具体方法可以百度一下。当然还有其他方法就是只导出表结构然后通过“导出数据\导入数据”的方法同步数据。

注意:如果使用“导出数据\导入数据”的方法同步数据,注意勾选“启用标示插入”

7.其它

1.如果存在alwayson记得将新的数据库加入到可用性数据库组中。

2.将新的数据库加入到备份作业中。

3.对比新旧两个数据库的表数量是否相同。

4.配置登入用户新的数据库权限。

总结

内存表是2014新引入的功能所以对于新功能的第一个版本使用要比较慎重,特别是在线上环境。虽然在上线之前做过测试,但是显然备份这块的测试往往比较容易被忽略因为没有线上的这种环境。好在是这次影响的是一个新上的项目数据量和并发都很小且允许节假日停机维护;如果是非常大的系统对于需要导入导出数据肯定是非常头疼的事情关键还得看允许停机的时长。因为自己在生产环境踩了坑,写这篇文章希望后面的人可以避免踩坑。

备注:内存表在2014版本的alwayson中无法同步到辅助副本,这就导致了它的作用大打折扣,2016版本可以同步到辅助副本,建议有条件的直接上2016。

备注:

作者:pursuer.chen

博客:http://www.cnblogs.com/chenmh

本站点所有随笔都是原创,欢迎大家转载;但转载时必须注明文章来源,且在文章开头明显处给明链接。

《欢迎交流讨论》

SQL Server 在Alwayson上使用内存表"踩坑"的更多相关文章

  1. 对于SQL Server,我需要多少内存

    经常被问到的一个问题:对于SQL Server,我需要多少内存?这个问题还是有同样的典型的“看情况而定”答案.在今天的文章里,我们来详细看下“看情况而定的”的不同方面. 全新SQL Server安装 ...

  2. SQL Server 2012 AlwaysOn集群配置指南

    1. AlwaysOn介绍 AlwaysOn是SQL Server 2012提供的全新综合.灵活.高效经济的高可用性和灾难恢复解决方案.它整合了镜像和群集的功能,基于OS 故障转移群集(Windows ...

  3. [转]SQL Server 性能调优(内存)

      存储引擎自调整 sql server 是如何分配内存的 32bit地址空间的限制 用户模式vas分配和virtualalloc 非boffer pool 分配内存(保留内存) VAS调整 AWE ...

  4. 服务器搭建域控与SQL Server的AlwaysOn环境过程(四)配置AlwaysOn

    0 引言 这一篇才真正开始搭建AlwaysOn,前三篇是为搭建AlwaysOn 做准备的. 步骤 1.3 配置AlwaysOn 请先使用本地用户Administrator登录这两个集群节点并执行下面的 ...

  5. 干货 | RDS For SQL Server单库上云

    数据库作为核心数据的重要存储,很多时候都会面临数据迁移的需求,例如:业务从本地迁移上云.数据中心故障需要切换至灾备中心.混合云或多云部署下的数据同步.流量突增导致数据库性能瓶颈需要拆分-- 本文将会一 ...

  6. SQL Server的AlwaysOn错误19456和41158

    SQL Server的AlwaysOn错误19456和41158 最近在公司搞异地数据库容灾,使用AlwaysOn的异地节点进行数据同步,在搭建的过程中遇到了一些问题 软件版本 SQL Server2 ...

  7. 利用HAProxy代理SQL Server的AlwaysOn辅助副本

    利用HAProxy代理SQL Server的AlwaysOn辅助副本 公司最近数据库升级到SQL Server2014 ,并部署了alwayson高可用集群 机房内有三套程序需要读取数据库 第一套:主 ...

  8. SQL SERVER 2012 AlwaysOn - 维护篇 03

    搭建 AlwaysOn 是件非常繁琐的工作,需要从两方面考虑,操作系统层面和数据库层面,AlwaysOn 非常依赖于操作系统,域控,群集,节点等概念: DBA 不但要熟悉数据库也要熟悉操作系统的一些概 ...

  9. 从0开始搭建SQL Server 2012 AlwaysOn 第三篇(安装数据,配置AlwaysOn)

    这一篇是从0开始搭建SQL Server 2012 AlwaysOn 的第三篇,这一篇才真正开始搭建AlwaysOn,前两篇是为搭建AlwaysOn 做准备的 操作步骤: 1.安装SQL server ...

随机推荐

  1. 设备offline时如何自动重置

    在linux底层 Linux/include/uapi/linux/usbdevice_fs.h中,重置_IO('U', 20)可以重置usb设备. 因此,我们可以在脚本中利用这个方法去重置USB 代 ...

  2. MAC Mysql 重置密码

    使用mac电脑,当mysql登录密码忘记时,需要重置密码.步骤如下: 1. 关闭当前正在运行的mysql进程. A.进入"偏好设置",选择mysql, 再选"stop m ...

  3. Swift try try! try?使用和区别

    Swift try try! try?使用和区别 一.异常处理try catch的使用 1. swift异常处理 历史由来 Swift1.0版本 Cocoa Touch 的 NSError ,Swif ...

  4. java面试题—精选30道Java笔试题解答(二)

    摘要: java面试题-精选30道Java笔试题解答(二) 19. 下面程序能正常运行吗() public class NULL { public static void haha(){ System ...

  5. JVM 堆内存,参数优化

    Java堆内存 http://www.importnew.com/19593.html JVM诊断之查看运行参数 JVM 垃圾回收器工作原理及使用实例介绍 https://www.ibm.com/de ...

  6. 浅谈JavaScript匿名函数与闭包

    一. 匿名函数   //普通函数定义: //单独的匿名函数是无法运行的.就算运行了,也无法调用,因为没有名称. 如: function(){             alert('123');    ...

  7. hdu2389二分图之Hopcroft Karp算法

    You're giving a party in the garden of your villa by the sea. The party is a huge success, and every ...

  8. 编写一个简单的java服务器程序

    import java.net.*;import java.io.*; public class server{ ); //监听在80端口 Socket sock = server.accept(); ...

  9. 转:MySQL表名不区分大小写

    在LINUX下调一个程序老说找不到表,但是我明明是建了表的,在MYSQL的命令行下也可以查到,为什么程序就找不到表呢? 一.linux中mysql大小写详情: 1.数据库名严格区分大小写 2.表名严格 ...

  10. 浅谈RSA加密

    RSA背景 在1976年以前,传统的加解密过程是: 1.A采用某种手段对数据进行加密. 2.数据传输到B的手中. 3.B逆向的实施A加密采用的步骤. 4.数据被还原. 这就是所谓的对称加密. 解密和加 ...