版本比较

标识

  • 该行被添加。
  • 该行被删除。
  • 格式已经改变。

SQLServer 企业版无法使用所有 CPU 内核

问题现象:

SqlServer不能使用操作系统CPU最大内核数,无法发挥服务器最佳性能,计算资源闲置浪费。

问题原因:

因从SqlServer 2012之后的版本,企业版本分为 Entetprise CAL Edition 和 Entetprise Core Edition,其中Entetprise CAL Edition 许可限制SqlServer只能使用物理CPU 20核数,逻辑处理器最大为40,Entetprise Core Edition版本则是无限制操作系统最大的内核数。

 SQL Server 版本划分的计算能力限制 - SQL Server | Microsoft Docs

具体说明,可参考上述微软官方KB

适用范围:

美宜佳所有相关SqlServer 2012以上版本的数据库。

适用版本:

SqlServer 2012 以上的Entetprise Edition, 注意:SqlServer 2012以下版本不受影响。

如何查询CPU最大核数和SQLServer使用最大内核数?

查看正在使用的CPU逻辑处理器

代码块
languagesql
use master
select * from sys.dm_os_schedulers where is_online=1 and status='VISIBLE ONLINE'

查看无法使用的CPU逻辑处理器

代码块
languagesql
use master
select * from sys.dm_os_schedulers where is_online=0

或是通过Windows任务管理器--性能--进行查看当前CPU内核数。

问题解决方法

进行SqlServer序列号更换,升级为SqlServer Entetprise Core Edition

重要注意事项:升级时SQLSERVE服务要处于运行状态。

升级前需要关闭数据库相应的活动会话,包含前端程序,数据库作业,确保没有活动事务和活动会话正在进行,因为升级过程会重启SQLSERVER数次,以防止数据库出现实例恢复,增加风险,整机升级过程在10分钟以内。

具体升级步骤:

  1. 加载SqlServer安装镜像包,要注意对应相应的版本
  2. 打开CMD,cd 目录,切换到虚拟光驱盘,运行以下命令进行版本升级。

Setup.exe /q /ACTION=editionupgrade /INSTANCENAME=MSSQLSERVER /PID="TBR8B-BXC4Y-298NV-PYTBY-G3BCP" /IACCEPTSQLSERVERLICENSETERMS

其中 /INSTANCENAME 为SQLSERVER实例名,一般默认为MSSQLSERVER

PID则为需要升级的序列号,进行相应的替换即可。

      3.升级完成后,可再次通过上述SQL查询可用的处理器,正常情况下可以看到所有的逻辑处理器都为'VISIBLE ONLINE' 状态,也可以通过select @@version进行查看当前数据库版本

     4. 检查数据库运行状态和前端程序是否正常运行。

清理无法删除的作业

代码块
languagesql
USE [msdb]
DECLARE @job_name VARCHAR(100)
SET @job_name = N'backup_diff.Subplan_1'    -- SELECT * FROM sysjobs_view; 可以通过这句查询作业名称,直接拷过来,避免手动输错
 
--注:jobName为维护计划对应的jobName
--删除在计划里面的日志
DELETE
    sysmaintplan_log
FROM
    sysmaintplan_subplans AS subplans
    INNER JOIN sysjobs_view AS syjobs ON subplans.job_id = syjobs.job_id
    INNER JOIN sysmaintplan_log ON subplans.subplan_id =sysmaintplan_log.subplan_id
WHERE
    (syjobs.name = @job_name)
 
--删除代理的作业
DELETE
    sysjobschedules FROM sysjobs_view v
    INNER JOIN sysjobschedules o ON v.job_id=o.job_id
WHERE
    v.name=@job_name
 
--删除子计划
DELETE
    sysmaintplan_subplans
FROM
    sysmaintplan_subplans AS subplans    
    INNER JOIN sysjobs_view AS syjobs ON subplans.job_id = syjobs.job_id
WHERE
    (syjobs.name = @job_name)
 
--删除作业
DELETE FROM msdb.dbo.sysjobs_view WHERE name = @job_name

SELECT * FROM sysjobs_view

用户导出与导入

DBA 经常会遇到的一个场景就是数据库的迁移,把数据库从A服务器迁移到B服务器。在数据库完成迁移后,我们还需要把相关的其他一些服务器级别的对象迁移过去,比如作业,登录账户,链接服务器等。今天主要分享导出登录账户的脚本

代码块
languagesql
/*
--SQL 数据库导出迁移用户账户及权限
--EG. [sp_help_revlogin]
*/

--sp_hexadecimal
USE master;
GO
IF OBJECT_ID('sp_hexadecimal') IS NOT NULL
DROP PROCEDURE sp_hexadecimal;
GO
CREATE PROCEDURE sp_hexadecimal
@binvalue VARBINARY(256),
@hexvalue VARCHAR(256) OUTPUT
AS
DECLARE @charvalue VARCHAR(256);
DECLARE @i INT;
DECLARE @length INT;
DECLARE @hexstring CHAR(16);
SELECT @charvalue = '0x';
SELECT @i = 1;
SELECT @length = DATALENGTH(@binvalue);
SELECT @hexstring = '0123456789ABCDEF';
WHILE (@i <= @length)
BEGIN
DECLARE @tempint INT;
DECLARE @firstint INT;
DECLARE @secondint INT;
SELECT @tempint = CONVERT(INT, SUBSTRING(@binvalue, @i, 1));
SELECT @firstint = FLOOR(@tempint / 16);
SELECT @secondint = @tempint - (@firstint * 16);
SELECT @charvalue = @charvalue + SUBSTRING(@hexstring, @firstint + 1, 1) + SUBSTRING(@hexstring, @secondint + 1, 1);
SELECT @i = @i + 1;
END;
SELECT @hexvalue = @charvalue;
GO

----- sp_help_revlogin

IF OBJECT_ID('sp_help_revlogin') IS NOT NULL
DROP PROCEDURE sp_help_revlogin;
GO
CREATE PROCEDURE [dbo].[sp_help_revlogin] @login_name sysname = NULL
AS
DECLARE @name sysname;
DECLARE @type VARCHAR(1);
DECLARE @hasaccess INT;
DECLARE @denylogin INT;
DECLARE @is_disabled INT;
DECLARE @PWD_varbinary VARBINARY(256);
DECLARE @PWD_string VARCHAR(514);
DECLARE @SID_varbinary VARBINARY(85);
DECLARE @SID_string VARCHAR(514);
DECLARE @tmpstr VARCHAR(1024);
DECLARE @is_policy_checked VARCHAR(3);
DECLARE @is_expiration_checked VARCHAR(3);
DECLARE @defaultdb sysname;
IF (@login_name IS NULL)
DECLARE login_curs CURSOR FOR
SELECT p.sid,
p.name,
p.type,
p.is_disabled,
p.default_database_name,
l.hasaccess,
l.denylogin
FROM sys.server_principals p
LEFT JOIN sys.syslogins l
ON (l.name = p.name)
WHERE p.type IN ( 'S', 'G', 'U' )
AND p.name <> 'sa';
ELSE
DECLARE login_curs CURSOR FOR
SELECT p.sid,
p.name,
p.type,
p.is_disabled,
p.default_database_name,
l.hasaccess,
l.denylogin
FROM sys.server_principals p
LEFT JOIN sys.syslogins l
ON (l.name = p.name)
WHERE p.type IN ( 'S', 'G', 'U' )
AND p.name = @login_name;
OPEN login_curs;
FETCH NEXT FROM login_curs
INTO @SID_varbinary,
@name,
@type,
@is_disabled,
@defaultdb,
@hasaccess,
@denylogin;
IF (@@fetch_status = -1)
BEGIN
PRINT 'No login(s) found.';
CLOSE login_curs;
DEALLOCATE login_curs;
RETURN -1;
END;
SET @tmpstr = '/* sp_help_revlogin script ';
PRINT @tmpstr;
SET @tmpstr = '** Generated ' + CONVERT(VARCHAR, GETDATE()) + ' on ' + @@SERVERNAME + ' */';
PRINT @tmpstr;
PRINT '';
WHILE (@@fetch_status <> -1)
BEGIN
IF (@@fetch_status <> -2)
BEGIN
PRINT '';
SET @tmpstr = '-- Login: ' + @name;
PRINT @tmpstr;
IF (@type IN ( 'G', 'U' ))
BEGIN -- NT authenticated account/group 
SET @tmpstr
= 'CREATE LOGIN ' + QUOTENAME(@name) + ' FROM WINDOWS WITH DEFAULT_DATABASE = [' + @defaultdb + ']';
END;
ELSE
BEGIN -- SQL Server authentication
SET @PWD_varbinary = CAST(LOGINPROPERTY(@name, 'PasswordHash') AS VARBINARY(256));
EXEC sp_hexadecimal @PWD_varbinary, @PWD_string OUT;
EXEC sp_hexadecimal @SID_varbinary, @SID_string OUT;
SELECT @is_policy_checked = CASE is_policy_checked
WHEN 1 THEN
'ON'
WHEN 0 THEN
'OFF'
ELSE
NULL
END
FROM sys.sql_logins
WHERE name = @name;

SELECT @is_expiration_checked = CASE is_expiration_checked
WHEN 1 THEN
'ON'
WHEN 0 THEN
'OFF'
ELSE
NULL
END
FROM sys.sql_logins
WHERE name = @name;

SET @tmpstr
= 'CREATE LOGIN ' + QUOTENAME(@name) + ' WITH PASSWORD = ' + @PWD_string + ' HASHED, SID = '+ @SID_string + ', DEFAULT_DATABASE = [' + @defaultdb + ']';

IF (@is_policy_checked IS NOT NULL)
BEGIN
SET @tmpstr = @tmpstr + ', CHECK_POLICY = ' + @is_policy_checked;
END;
IF (@is_expiration_checked IS NOT NULL)
BEGIN
SET @tmpstr = @tmpstr + ', CHECK_EXPIRATION = ' + @is_expiration_checked;
END;
END;
IF (@denylogin = 1)
BEGIN -- login is denied access 
SET @tmpstr = @tmpstr + '; DENY CONNECT SQL TO ' + QUOTENAME(@name);
END;
ELSE IF (@hasaccess = 0)
BEGIN -- login exists but does not have access 
SET @tmpstr = @tmpstr + '; REVOKE CONNECT SQL TO ' + QUOTENAME(@name);
END;
IF (@is_disabled = 1)
BEGIN -- login is disabled 
SET @tmpstr = @tmpstr + '; ALTER LOGIN ' + QUOTENAME(@name) + ' DISABLE';
END;

select @tmpstr=@tmpstr+ N';EXEC sp_addsrvrolemember N''' +sp.name+ ''' ,N''' + rsp.name+'''; '
FROM sys.server_principals sp 
LEFT JOIN sys.server_role_members srm ON sp.principal_id=srm.member_principal_id 
LEFT JOIN sys.server_principals rsp ON srm.role_principal_id=rsp.principal_id 
where rsp.name is not NULL
AND sp.name=@name

PRINT @tmpstr;
END;
FETCH NEXT FROM login_curs
INTO @SID_varbinary,
@name,
@type,
@is_disabled,
@defaultdb,
@hasaccess,
@denylogin;
END;
CLOSE login_curs;
DEALLOCATE login_curs;
RETURN 0;

--生成创建登录账号脚本
exec master..sp_help_revlogin

Image Added

把脚本COPY到新的服务器上执行

Image Added

总结

使用这个办法有2个好处。

1.可以批量同步所有需要的登录账号 

2.由于产生的SID是相同的,不会有孤立账号的问题。在ALWAY ON环境下,我们就需要采用这种方式。不然没有ALWAYS ON 切换后都需要手动处理 孤立账号的问题

作业导出与导入

在SSMS 中,选中如下  作业的文件夹

Image Added

然后按F7 ,就会出现下面的界面,然后按CTRL 选中需要导出的作业

Image Added

右键 编写作业为--》 CREATE 到

Image Added

就能生成所有需要的作业的脚本,然后在迁移后的服务器上面执行。

注意事项
如果作业的所有者是windows 账号,那么迁移后由于服务器名称不一样,可能不存在相同的登录账号。

在执行脚本时会提示如下错误:

Image Added

把@owner_login_name 修改为异地节点存在的账号,问题就解决。

DBLink导出与导入

在SSMS 中,选中如下  链接服务器 的文件夹

Image Added

然后按F7 ,就会出现下面的界面,然后按CTRL 选中需要导出的链接服务器

Image AddedImage Added

然后把生成的脚本,COPY到新的服务器上面,然后将远程登录密码改为实际密码(脚本中已替换为########,不可直接执行),执行就可以了。在建立完成后一定要测试链接服务器是不是可用。

Image Added

临时完全备份

代码块
languagesql
USE master;
GO
DECLARE @FileName NVARCHAR(256);
SET @FileName = N'E:\backup\备份数据库名_FullBackup_20200707.bak';
BACKUP DATABASE [备份数据库名]
TO DISK = @FileName
WITH COMPRESSION,
INIT,
SKIP,
FORMAT;
GO

DECLARE @backupSetId AS INT;
SELECT @backupSetId = position
FROM msdb..backupset
WHERE database_name = N'备份数据库名'
AND backup_set_id =
(
SELECT MAX(backup_set_id)
FROM msdb..backupset
WHERE database_name = N'备份数据库名'
);
IF @backupSetId IS NULL
BEGIN
RAISERROR(N'验证失败。找不到数据库“备份数据库名”的备份信息。', 16, 1);
END;
RESTORE VERIFYONLY
FROM DISK = N'E:\backup\备份数据库名_FullBackup_20200707.bak'
WITH FILE = @backupSetId,
NOUNLOAD,
NOREWIND;
GO

删除数据模板

删除表数据需要再三确认,可以删除后先进行备份,在进行删除

代码块
languagesql
select * into table_name_bak  FROM  table_name  with (nolock);  --删除数据之前备份

当删除数据行数超过2万行时,为避免锁表阻塞,请使用循环删除的方法

脚本范例如下:

代码块
languagesql
DECLARE @coun INT;
DECLARE @i INT;
SET @i=0 ;
SELECT @coun=COUNT(*)/5000 FROM table_name with (nolock) ; --汇总需要删除的数据行数,从而得到需要循环次数
WHILE @i<@coun+1                        
begin
      DELETE TOP (5000) FROM  table_name ;  --每次删除5000行

  SET @i=@i+1 ;

  WAITFOR DELAY '00:00:01';  --每次循环延迟1S后继续循环

END

表关联删除,删除时必须是where 条件,不能使用 ON

代码块
languagesql
DECLARE @coun INT;
DECLARE @i INT;
SET @i=0 ;
SELECT @coun=COUNT(*)/5000 FROM aa_test a , ab_test b WHERE a.id=b.id ; --汇总需要删除的数据行数,从而得到需要循环次数 
WHILE @i<@coun+1                        
begin
      DELETE TOP (5000) a FROM aa_test a , ab_test b WHERE a.id=b.id  ;  --每次删除5000行 ,删除时必须是where 条件,不能使用 ON

  SET @i=@i+1 ;

  WAITFOR DELAY '00:00:01';  --每次循环延迟1S后继续循环

END

建议删除较大数据接入相关负责人提醒(短信或邮件)



目录