缘起 早在2015年的时候,随着阿里云业务突飞猛进的发展,SQLServer业务也积累了大批忠实客户,其中一些体量较大的客户在类似大促的业务高峰时RDS的单机规格(规格是按照 内存CPUIOPS 一定比例分配,根据底层资源不同都会有各自上限)已经不能满足用户的业务需求,在我们看来也需要做Scale Out了,但SQLServer并没有完备的中间件产品,所以无论是逻辑Sharding还是只读分离,都需要用户配合做应用改造,而从用户角度看Sharding改动量很大不是一时间能完成的,那么更多寄希望我们提供读写分离的方案来满足业务需求。 那么读写分离我们第一个想到的即是AlwaysOn技术,但由于当时AlwaysOn对域控和Windows群集都是强依赖,而这两者又对我们所依赖的基础设施有很大挑战,需要做很多的突破产品限制的非标准化操作才有可能实现并且还有安全风险,所以最后我们只能放弃AlwaysOn技术方案,重新设计方案帮助用户度过难关。 最后,面对这类客户需求我们的方案如何产品化是值得我们思考的。 产品快速发展 除了读写分离,产品上还有很多更重要的问题急需我们去解决,所以从2015年到2017年我们经历了一个飞速发展阶段,围绕产品稳定性、多样性以及用户体验做了非常多的事情,举几个点: 为了提高稳定性和用户体验我们最先替换了底层架构,这也为后续产品多样化发展打下基础 为了满足不同用户需求,推出了SQLServer 2008R2/2012/2014/2016 Web/Standard/Enterprise 不同Version、Edition的组合版本 为解决上云难问题推出了上云评估工具,以及针对不同版本、不同场景的上云方案全量备份数据上云SQL Server 2008 R2版、全量备份数据上云SQL Server 2012及以上版本、增量备份数据上云SQL Server 2012及以上版本、SQL Server实例级别数据库上云 为了提升用户体验支持更多特性,我们在SQL层提供了很多封装的存储过程,这里有些看似简单的功能在面对外部的安全、内部的SQL镜像等因素的共同作用下,实现的挑战还是很大的 为了让专家服务更智能、更能贴近每个用户,我们研发了SQLServer CloudDBA集合了云上大量性能、空间问题的解决方案 在这当中依旧不断有读写分离的用户需求,每次遇到我们都先引导到了IaaS层用ECS自建实现,因为PaaS化的时机并不成熟,具体原因跟SQLServer当前的技术栈和云产品的结合有着密切的关系,这里也可以把我们背后的一些思考分享出来。 读写分离 首先明确我们讨论的读写分离是什么,MySQL的读写分离大部分是利用中间层做路由解析,基本上可以实现对应用端透明只有少部分场景需要用户做适配。 SQLServer并没有成熟的中间件产品,本质上讲是TDS(Tabular Data Stream)不完全开放的原因,如果要做也是有办法的,只是投入的成本远大于收益;基于此,SQLServer无论利用当前何种技术实现读写分离,对应用来讲都需要做一些适配,即使是使用AlwaysOn技术在链接驱动的参数配置上也会不同,所以我们后面讨论的读写分离都是基于这个前提。 技术选型 我们对比了SQLServer所有相关的技术栈 其中数据安全、HA(High Availability 高可用)、DR(Disaster Recovery 灾难恢复)以及备库是否可读是我们最关注的;这里的HA是指原生技术本身是否支持自动HA,当结合了部分云产品后我们也有能力把不支持变为支持,数据安全和灾难恢复的时间基本是原生技术决定的,备库是否可读是对单一技术的说明但做一些技术组合是可以把不可读变为可读的(比如Database Mirroring+Database Snapshots),最终综合来看Transactional Replication和AlwaysOn是我们觉得有机会做读写分离产品化的技术。 接着我们单独来看这两种技术对比 原理上讲Replication是逻辑复制,对比AlwaysOn的物理复制在性能、延迟、可靠性上都会有一定的差距;并且在产品复杂度读、可控性上和易用性上,由于Replication过于灵活细到表、列级别很难控制,无论用户使用还是我们做产品化整个复杂度非常高;所以最终我们选用AlwaysOn。 AlwaysOn技术 AlwaysOn是原生支持High Availability和Disaster Recovery的技术,本身又分为Failover Cluster Instances(后续简称FCI)和Availability Groups(后续简称AG),下面的图是FCI和AG的基础架构 其中FCI和常规版本的AG都依赖Windows Server Failover Clustering(后续简称WSFC),不同点是FCI是Share Storage而AG是Share Nothing,FCI是实例级别同步而AG是DB级别,那么很容易想到Share Nothing会有同步和异步的区别(和镜像技术类似),其中两者的区别点需要我们知道AlwaysOn的基本同步过程 首先在Primary节点的日志(Commit/Log Block Write)会从Log Cache刷到磁盘,同时Primary节点的Log Capture也会把日志发送到其它所有Replica节点,对应节点的Log Receive线程把收到的日志同样从Log Cache刷到磁盘,最后会由Redo Thread应用这些日志刷到数据文件里。 这其中还有一步,就是在Secondary端刷日志的时候,如果Primary节点等待这次返回的Acknowlege Commit,那么就是同步模式,反之如果Primary端不等Secondary的返回那么就是异步模式,两者的区别由此展开。 这是基本的同步过程,但无论是AlwaysOn还是Database Mirroring都存在一种情况,即同步模式下如果Secondary端异常,Primary端没有收到他的心跳也没有收到这次的Acknowlege Commit,那么也并不会算作写入失败,因为它一旦认定Secondary异常就不会等这次ACK,而是退化为类似异步的模式,但会把Secondary端的异常状态记录在基表里,通过相关视图(sys.dm_hadr_database_replica_states、sys.database_mirroring)暴露出来,就是我们常见的NOT SYNCHRONIZING/Disconnect状态,这时候自动化运维系统或者DBA就需要做判断处理,等到Secondary修复重新联机后会向Primary报告End of Log (EOL) LSN,Primary端再向它发送EOL LSN 之后hardened的所有日志,一旦Secondary端开始接收到这些日志并逐步刷到日志文件中,那么整个AG或者Mirroring相关的视图又会标记其状态为Synchronizing,表明正在追赶直到Last Hardened (LH) LSN达到主备一致状态,这时重新回到同步模式。 以前的情况一直是这样,直到SQLServer 2017 CU 1引入了REQUIRED_SYNCHRONIZED_SECONDARIES_TO_COMMIT这个参数,参数名字很长但也基本包含了他的作用,应对刚才的场景是可以让Primary端一直等到Secondary节点重新联机并同步后在提供服务。 了解了AG同步、异步以及FCI,在总结下我们关心的点 在实际方案中这些也可以结合起来,最终再和阿里云产品整合做一个整体方案,之前也讲到阿里云从15年就开始做类似方案来解决用户问题,一直到最终PaaS化也过度了三个版本。 云上演进 第一版本我们使用了ECS、SSD云盘、OSS、VPC、SLB作为基础;在SQL技术上,我们使用SQL+WSFC+AD的方式,目前看这种方式支持的版本也非常多,从12到17都可以;验证方式既可以用域控也可以用证书。 但他有2个缺点:第一是成本高,除了Primary和两个Secondary节点还要有两个AD节点,毕竟我们每个环节都要保证高可用;第二点是稳定性不够,网络抖动的情况非常容易让WSFC判断异常,SQL端DB同时出现不可用; 这是第二版的架构,跟第一版相比我们用到了HAVIP来解决监听器问题,去掉了AD只能用证书做验证,但也因此最小资源开销降低到3;这个方案也是之前在阿里云上用的比较多的,但同第一个方案一样,在网络稳定性上会有很多挑战,因为我们未来面对的场景不只是同城跨可用区还会有更多跨Region以及打通海外的场景,所以这个方案也只能Cover一部分用户的需求,但对我们不是一个最终方案。 最终我们找到了方案三,去除了WSFC和AD,只关注基础云产品和SQL本身;最终要的是跟方案二相比,对网络的抖动敏感度会更低也更可控,最多是在Primary端出现Send Queue的堆积,这个我们完全可以通过SQLServer相关的Performance Counter监控并做一些修复调整。 但没有方案是完美的,可控性强的代价是这种无群集无域控架构原生是不具备HADR能力的,这点熟悉WSFC的同学可以知道之前架构的HA都是依赖WSFC,他包括健康检查、资源管理、分布式元数据通知维护以及故障转移,所以这时候就必须我们自己去解决这个问题;为此我们也做了很多努力,最终实现了支持AlwaysOn无域控无群集的HA系统,不依赖Cluster完全自主可控的HA。 产品化 最终的产品架构如下,首先会保证有2个同步节点做主备,并且尽量分配在不同的可用区,其它只读节点默认是异步,最多可以有7个只读节点;用户的访问链路可以有三种: 第一种是读写链路,会指向两个同步节点,由我们的HA来保证高可用 第二种是统一只读链路,根据用户需求设定,把指定的Replica节点绑定到一起按照一定的权重比例分配链接 第三种是单一只读链路,即每个只读节点会提供一个单独的链接,让用户也可以自己灵活配置,比如用户的APP Server就是在可用区A那么就可以直接访问可用区A的只读地址,避免再通过统一只读被路由到其它区域 至此SQLServer AlwaysOn已经在阿里云PaaS化,当然目前只是支持最主要功能,后续还有很多可以完善丰富的地方,希望有更多用户了解和使用这个产品并帮他们解决实际问题。
SQLServer如何获取客户端IP 很多用户询问如何通过SQLServer获取客户端IP从而定位一些问题,比如链接泄露,其实主要是利用几个相关视图,如下给出一些SQL方便用户排查 当前链接 SELECT CONNECTIONPROPERTY('PROTOCOL_TYPE') AS PROTOCOL_TYPE, CONNECTIONPROPERTY('CLIENT_NET_ADDRESS') AS CLIENT_NET_ADDRESS 所有链接 SELECT SP.SPID, SP.LOGINAME, SP.LOGIN_TIME, SP.HOSTNAME, SP.PROGRAM_NAME, DC.CLIENT_TCP_PORT, DC.CLIENT_NET_ADDRESS FROM SYS.SYSPROCESSES AS SP INNER JOIN SYS.DM_EXEC_CONNECTIONS AS DC ON SP.SPID = DC.SESSION_ID WHERE SP.SPID > 50 AND DC.AUTH_SCHEME='SQL' 查看更详细的链接参数配置 SELECT * FROM SYS.DM_EXEC_SESSIONS WHERE SESSION_ID=之前获取的SPID
背景 RDS FOR SQLServer 2012 已经上线一段时间了,从反馈来看大家遇到了很多权限相关的问题,所以建议把RDS FOR SQLServer 2012权限提升这篇文章作为基础知识先能够理解;再之后针对大家经常遇到的CASE我们会把解决方法分享出来,本篇文章是其中之一。 问题 用户应用链接数据库失败 持续报错,业务因此停滞,使用SSMS链接也出错 Detail =================================== Cannot connect to *** =================================== Cannot open user default database. Login failed. Login failed for user 'option_u'. (.Net SqlClient Data Provider) ------------------------------ For help, click: http://go.microsoft.com/fwlink?ProdName=Microsoft%20SQL%20Server&EvtSrc=MSSQLServer&EvtID=4064&LinkId=20476 ------------------------------ Server Name: *** Error Number: 4064 Severity: 11 State: 1 Line Number: 65536 ------------------------------ Program Location: at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction) at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose) at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady) at System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj) at System.Data.SqlClient.SqlInternalConnectionTds.CompleteLogin(Boolean enlistOK) at System.Data.SqlClient.SqlInternalConnectionTds.AttemptOneLogin(ServerInfo serverInfo, String newPassword, SecureString newSecurePassword, Boolean ignoreSniOpenTimeout, TimeoutTimer timeout, Boolean withFailover) at System.Data.SqlClient.SqlInternalConnectionTds.LoginNoFailover(ServerInfo serverInfo, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance, SqlConnectionString connectionOptions, SqlCredential credential, TimeoutTimer timeout) at System.Data.SqlClient.SqlInternalConnectionTds.OpenLoginEnlist(TimeoutTimer timeout, SqlConnectionString connectionOptions, SqlCredential credential, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance) at System.Data.SqlClient.SqlInternalConnectionTds..ctor(DbConnectionPoolIdentity identity, SqlConnectionString connectionOptions, SqlCredential credential, Object providerInfo, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance, SqlConnectionString userConnectionOptions, SessionData reconnectSessionData) at System.Data.SqlClient.SqlConnectionFactory.CreateConnection(DbConnectionOptions options, DbConnectionPoolKey poolKey, Object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningConnection, DbConnectionOptions userOptions) at System.Data.ProviderBase.DbConnectionFactory.CreateNonPooledConnection(DbConnection owningConnection, DbConnectionPoolGroup poolGroup, DbConnectionOptions userOptions) at System.Data.ProviderBase.DbConnectionFactory.TryGetConnection(DbConnection owningConnection, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal oldConnection, DbConnectionInternal& connection) at System.Data.ProviderBase.DbConnectionInternal.TryOpenConnectionInternal(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions) at System.Data.ProviderBase.DbConnectionClosed.TryOpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions) at System.Data.SqlClient.SqlConnection.TryOpenInner(TaskCompletionSource`1 retry) at System.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1 retry) at System.Data.SqlClient.SqlConnection.Open() at Microsoft.SqlServer.Management.SqlStudio.Explorer.ObjectExplorerService.ValidateConnection(UIConnectionInfo ci, IServerType server) at Microsoft.SqlServer.Management.UI.ConnectionDlg.Connector.ConnectionThreadUser() 解决 option_u是用户的默认账号,根据报错判断是option_u账号的默认数据库出现异常,为了确认我们通过后端Windows认证登陆用如下SQL判断下: SELECT SP.NAME, SP.TYPE_DESC, DB.STATE_DESC FROM SYS.SERVER_PRINCIPALS AS SP INNER JOIN SYS.DATABASES AS DB ON SP.DEFAULT_DATABASE_NAME = DB_NAME(DB.DATABASE_ID) WHERE SP.NAME = 'OPTION_U' 结果没有返回!那这样看来是用户设置了一个不存在的数据库作为默认数据库,修复方式如下: USE [master] GO ALTER LOGIN [option_u] WITH DEFAULT_DATABASE=[master] GO 注意修复的前提是用户首先要能登陆数据库,当然着急的情况通过工单让后端处理也是可行的,但实际如果手边有SSMS客户端(如果没有可以从 这里下载)可以通过指定数据库解决 触发场景 问题的解决方法大家已经清楚了,但为什么会出现这种情况即用户为什么要设置一个不存在的库作为默认数据库呢? 为了了解背后的原因我们了解了用户的场景并做了如下测试: 实例生产后在控制台创建初始账号 test 利用初始账号登陆登陆实例并创建数据库testdb SELECT SUSER_NAME() AS LOGIN_NAME GO CREATE DATABASE TESTDB GO 把test的默认数据库改为testdb USE [master] GO ALTER LOGIN [test] WITH DEFAULT_DATABASE=[testdb] GO 删除testdb DROP DATABASE [testdb] 再次尝试test登陆 至此原因也清楚了,用户并不是刻意设置一个不存在的数据库而是开始存在后来删除了 这里还有2个步骤值得分析和改进 步骤3用户为什么要把test账号的默认数据库改为testdb 这实际并不是一个通用的需求完全取决于应用,修改默认数据库后用户的链接字符串不需要配置DB参数,应用链接数据库后可以直接访问testdb下的object;反之也可以通过修改链接串的DB参数或者应用访问数据库对象的写法比如是DBNAME.Schema.Object(不支持link所以不需要servername),但这涉及的应用修改所以需要用户判断哪种更合适业务 步骤4删除testdb 我们知道SQLServer在一些情况会检查依赖关系,比如要删除某个账号但此账号在Trigger中有用到,那么删除就会失败并提示有引用依赖,但看起来login的default database没有在考虑范围内,微软虽然没做但考虑云产品的受众RDS可以通过一些方式实现给予用户更好的引导以提升体验,这也会作为后续的一个可改进点
SQLServer IN Windows Container初探 背景 微软早在14年就宣布与Docker合作把容器技术搬到Windows上,终于在去年9月Windows Server 2016中落地了这一承诺,推出了能与Docker相容的Container技术. 关于Windows Container具体也有2种类型Windows Containers Windows Server Containers – provide application isolation through process and namespace isolation technology. A Windows Server container shares a kernel with the container host and all containers running on the host. Hyper-V Containers – expand on the isolation provided by Windows Server Containers by running each container in a highly optimized virtual machine. In this configuration the kernel of the container host is not shared with the Hyper-V Containers. 简单理解Server Containers更像是Linux上的Docker容器-共享kernel,而Hyper-V Containers则是有自己独立的kernel,其它关于隔离性、安全性等区别也基于此扩展开。 后续的实验部分我们都是在阿里云ECS上使用Windows Server Containers做测试 --ECS OS Versin Windows Server 2016 Datacenter <X64> --SQLServer Versin Microsoft SQL Server vNext (CTP1.4) - 14.0.405.198 (X64) Docker --可用的providers get-packageprovider Install-Module -Name DockerMsftProvider -Repository PSGallery -Force --安装Docker Install-Package -Name docker -ProviderName DockerMsftProvider -Verbose --重启生效 Restart-Computer -Force --利用Windows 2016的OneGet(PackageManagement)找到最后我们需要的Docker --前两个步骤可以看出OneGet实际更像是一个管理包管理的工具 --已安装的包 get-package 这里对比Linux Docker有不同,Linux Docker中base image可以自己做但Windows上目前只能从官方获取 --base image microsoft/windowsservercore microsoft/nanoserver more microsoft image SQLServer microsoft/mssql-server-windows --拉取镜像 docker pull microsoft/mssql-server-windows 这个过程需要一些时间主要是镜像源在国外,所以测试中我使用的是香港的ECS尽量快些 如果想自己构建安装参考 mssql-server-windows/dockerfile 从这里申请下载SQLServer安装 SQL Server vNext Community Technology Preview #启动SQLServer docker run -d -p 1433:1433 -e sa_password=*** -e ACCEPT_EULA=Y microsoft/mssql-server-windows #容器网络配置 GET-VMSwitch GET-NetNat Get-ContainerNetwork 测试中使用的是NAT模式,其它网络模式参考 Windows Container Networking 容器内部访问 #Windows 账号认证登录 docker exec -it <DOCKER_CONTAINER_ID> sqlcmd #SQL 账号认证登录 docker exec -it <DOCKER_CONTAINER_ID> sqlcmd -S. -Usa 容器外部-宿主机访问 宿主机通过 SQL Server Management Studio (SSMS)访问,这一客户端程序在2016后已经作为单独的组件提供,对应安装包已经不再集成 NEW SSMS #获取到的信息包括刚才的密码,需要注意 docker inspect 86bb05abfd3d28187742ebb60b2a6fefb80596644b1469c94099ada408217602 #根据template只解析容器的私网ip docker inspect -f '{{ .NetworkSettings.Networks.nat.IPAddress }}' 86bb05abfd3d28187742ebb60b2a6fefb80596644b1469c94099ada408217602 容器外部-跨机访问 #链接容器宿主机的外网IP 从SQLServer上也可以明显的看出几种访问方式不同 session51是容器内Windows认证访问,client address解析为local machine session52、53是容器内SQL认证访问,client address解析为容器的私网IP session54是 容器外部-跨机访问,client address解析为来源公网IP session55是 容器外部-宿主机访问,client address解析为宿主机的虚网卡地址,对每个容器来说相当于一个vSwitch,容器的vNIC都会链接到这个Hyper-V Virtual Switch 注意:跨机访问时保证windows防火墙不要block链接,阿里云安全组规则也要设置正确,本例是对特定IP做了授权 问题 对于Container隔离性的选择 从安全上讲hyper-v container更合适公有云;Server Container 更适合私有云内部使用,适合可信多租户、应用间彼此相互信任、应用在容器中不违反隔离边界,这里可以分解为2点: 应用安全性 隔离有效性 业内实际案例Azure自然提供了对应的公有云服务且是基于hyper-v container的,对于业务场景来说目前只看到携程有实际案例Windows Container在携程的应用 SQLServer在Container中的表现能否满足用户需求这个在后续会给出一些测试 Licence问题在Windows Container中提供SQLServer服务具体的Licence计费方式还不清晰
问题背景 SQLServer的日志是DB级别的这点和MySQL不同,多个DB就需要维护多个日志文件并且每个DB的日志文件可以有多个,所以从空间资源的角度来说这给云上的用户带来一些困扰,毕竟在云上磁盘空间是要收费的,除了性能他们也非常关心这些日志何时截断重用、何时收缩 问题探讨 事务日志 在理解事务日志基本概念的基础上我们来做个有关收缩的测试: --1. 构建测试用的日志 -- tpcc是我的测试DB,ITEM表是之前生成的一个测试表,这里不需要关心表内容 USE [tpcc] GO DECLARE @VALUE INT SET @VALUE = 0 WHILE @VALUE <= 100 BEGIN SELECT * INTO ITEM_2 FROM ITEM DROP TABLE ITEM_2 SET @VALUE = @VALUE + 1 END GO --2. 观察当前等待日志截断重用的原因和每个DB的日志使用量 select log_reuse_wait_desc from sys.databases where name='tpcc' go dbcc sqlperf(logspace) go --3. 备份数据和日志 BACKUP DATABASE TPCC TO DISK='D:\BACKUP\TPCC.BAK' WITH COMPRESSION,INIT BACKUP LOG TPCC TO DISK='D:\BACKUP\TPCC.TRN' WITH COMPRESSION,INIT --4. 再次观察当前等待日志截断重用的原因和每个DB的日志使用量 select log_reuse_wait_desc from sys.databases where name='tpcc' go dbcc sqlperf(logspace) go --5. 收缩日志 USE [tpcc] GO dbcc shrinkfile(log) --6. 查看收缩后的日志使用量 dbcc sqlperf(logspace) go 这一步也可以通过上一步收缩的结果计算出来(90112*8K = 704 MB); 现在在理解基本概念的基础上产生了第一个疑问,为什么1G的日志使用了4%却只能收缩到704MB? 为了解答这个问题我们需要引入另一个概念 Virtual Log Files(VLF),SQLServer为了方便日志管理,逻辑上将事务日志划分为多个虚拟日志文件,我们要讨论的收缩就是以虚拟日志文件为单位操作的。 具体可以参考日志的结构 我们再来重复一次刚才的测试,这次加入对VLFs的记录观察; 在刚才的1、3、5步骤之后记录如下SQL返回结果,帮助后续分析; USE [tpcc] GO dbcc loginfo 步骤1结束后 步骤3结束后 步骤5结束后 这里跟收缩日志最相关的是Stats字段,0表示inactive,2表示active,active状态的VLF通过checkpoint+日志备份可以转化为inactive,但有一个原则是header(可理解为最新的活动日志)部分只能后推或回绕(wraps around)不能move forward(跟offset相关); 在开始备份前有11个活动VLF,checkpoint(数据备份的第一阶段)+ 备份日志后只有最新的VLF处于活动状态(日志截断并标记老的VLF为inactive-可重用),收缩后释放掉了未使用的VLF; 截止到这里截断和收缩实际已经达到了充分利用空间的目的,虽然直观看物理文件没有下降多少但新的日志已经开始回绕;但即便如此一些客户还在追求物理文件的进一步减少,实际想达到这个目的也很简单,依据之前讲的header部分已经回绕,再次备份收缩就可以了; --再次备份 backup database tpcc to disk='d:\backup\tpcc.bak' with compression,init backup log tpcc to disk='d:\backup\tpcc.trn' with compression,init --再次收缩 USE [tpcc] GO dbcc shrinkfile(log) --观察VLF USE [tpcc] GO dbcc loginfo 为什么没有收缩最后一个inactive的VLF跟这个DB的日志初始化大小、shrinkfile的参数有关,简单说不回收最后一个VLF已经可以达到初始化的大小; 我们也可以通过VLF的总大小和物理文件对比做一个验证: --通过FileSize计算要加8KB的页头 67043328+67043328+67043328+67043328+67043328+67043328+67043328+67559424+8192=536870912B=512MB --通过最后一个偏移量算 469311488+67559424=536870912B=512MB 结论和建议 日志截断依赖于checkpoint和日志备份(FULL模式) 日志截断的含义是把VLF标记为可重用 日志收缩的多少需要看VLF的header和VLF的数量以及大小而不是通过sqlperf返回的结果判断(这也是很多DBA和用户误解的地方) VLF的数量是事务日志创建时初始化好的一个初始值后续会随日志增长和增加,大小跟日志文件的大小、增长速度相关没有恒等的计算方法 在非云场景下(传统用户),不建议用户频繁做日志收缩去回收空间,因为这种收缩是有很大开销的,但肯定也不会一直增长,我们通过频繁的日志备份做截断、回绕日志文件以达到节省空间的目的;在云场景下(RDS),依然也不建议频繁收缩,但面对一些希望用其它资源换空间资源且业务场景允许的情况下,建议这部分用户使用OpenAPI定制化自己的备份策略
先用DATEADD拿到你想要的时间,在用convert转换成你想要的时间格式,对应你的需求类似如下:
select DATEADD(yy,-1,GETDATE()) as lastyear1
select convert(varchar, DATEADD(yy,-1,GETDATE()), 112) as lastyear2
select DATEADD(mm,-1,GETDATE()) as lastmonth1
select convert(varchar, DATEADD(mm,-1,GETDATE()), 112) as lastmonth2
以Admin身份打开Powershell执行如下脚本完成NetFX的安装(同时要注意保证commit mem足够2G,如果不够就开启虚拟内存让他至少达到2G),再装SQLServer即可。
Set-ItemProperty -Path 'HKLM:SOFTWAREPoliciesMicrosoftWindowsWindowsUpdateAU' -Name UseWUServer -Value 0
Restart-Service -Name wuauserv
Install-WindowsFeature Net-Framework-Core
Set-ItemProperty -Path 'HKLM:SOFTWAREPoliciesMicrosoftWindowsWindowsUpdateAU' -Name UseWUServer -Value 1
Restart-Service -Name wuauserv