🪟 第3章 SQL Server服务的启动与注册
📄 一句话概括

一个「实例」在 Windows 里不是一个进程,是一组服务:引擎、代理、Browser、全文、报表。 服务账户决定这个服务能读哪些目录、能不能碰网络共享,填错了备份就写不进去。 启动类型只有三个数要认:2 开机自动、3 手动按需、4 禁用;自动里还能勾「延迟启动」。

📝 本章和前面几章的关系

第 2 章装完,服务已经在跑了。 这一章退一步看它:这个服务在哪儿、用谁的身份跑、什么时候起。这三件事都在「服务」面板里, 也都能在注册表里读到。 教材 3.1 分三小节:服务与实例的关系、服务账户、启动类型。 三节其实在回答同一个问题——出问题时先看哪儿。 顺序也是排查的顺序。 本沙箱没装 SQL Server,但注册表里的服务表是真的。 这一节的数全是从这台机器上读出来的,不是抄的规格表;涉及真实例的地方会明确标出来。

一、一个实例 = 一组服务

装完 SQL Server,服务列表里会多出几条名字都带 SQL 的项。 它们不是并列的,是围绕同一个实例的一组。

服务名 什么时候有 停了会怎样
SQL Server (MSSQLSERVER) 引擎本体,必装 所有连接全部断开
SQL Server 代理 标准版及以上 定时作业不跑,备份停摆
SQL Server Browser 有命名实例时 命名实例连不上
SQL Server 全文搜索 选了全文组件 CONTAINS 直接报错
SQL Server 报表服务 选了报表组件 报表门户打不开

第一行是核心,其余都是配套。 括号里的 MSSQLSERVER 就是实例名——默认实例用这个名字, 命名实例会变成 SQL Server (SQLEXPRESS) 这种写法。 注意方括号外那层括号。 服务全名是 SQL Server (MSSQLSERVER), 但用命令行启动时敲的是里面的 MSSQLSERVER:net start MSSQLSERVER。 少了这层括号,命令行会告诉你「服务名无效」。 命名实例的服务名规则不一样:注册表里直接叫 MSSQL$SQLEXPRESS, 用的是美元符号,不是括号。所以「默认实例用括号、命名实例用美元」——这两套写法经常被记混, 记混的结果就是第 3.2 节那个返回码 2。

二、服务账户:决定它能碰什么

每个服务都有一个「登录身份」,在注册表里叫 ObjectName。 它决定这个服务能读哪些目录、能不能访问网络共享、能不能写事件日志。 本机 782 个服务的分布是这样的:

运行账户 服务个数 权限范围
(未设置) 507 驱动类服务,不起用户态进程
LocalSystem 168 本机最高权限,网络上是机器身份
LocalService 48 本机低权限
专用域账户等 35 按授权来,最精确

第一行的 507 不是「没配好」,是驱动类服务本来就不需要账户。它们跑在内核里,没有用户态进程, 也就没有登录身份这回事。 SQL Server 装的时候让你选的就是这一项。 教材给的两个选项是「内置账户」和「当前用户」, 实际安装向导里是四个:NT AUTHORITY\NetworkService、NT AUTHORITY\LocalSystem、专用域账户、 NT Service\MSSQLSERVER。 2012 之后默认是第四个,也就是虚拟账户。 它不是真实存在的用户,是系统按服务名造出来的一个身份, 权限只到「这台机器上跟这个服务相关的东西」。好处是不用管密码,也不用担心密码过期。 选错了最典型的症状是备份写不进去。 备份目录在 D 盘,权限只给了 Administrators, 而服务跑在 NetworkService 下面——备份作业会报「拒绝访问」,日志里只有一行 5 号错误。 这种问题查起来很费时间,因为它不在 SQL 里,在文件系统上。

三、启动类型:只有三个数要认

Start 值 含义 什么时候启动
0 引导 系统加载器拉起,驱动层
1 系统 内核初始化时,服务层最底层
2 自动 开机后由服务控制管理器拉起
3 手动 有人调用才起,含「按需触发」
4 禁用 谁都起不来,连手动都不行

真正要记的是 2、3、4 三个。 0 和 1 是驱动和内核组件的,用户在服务面板里改不了。 「自动」里还有一个隐藏选项叫「延迟启动」。 它仍然是 Start=2, 但注册表里多一个 DelayedAutostart=1,意思是开机后等两分钟再起。 SQL Server 不勾它,因为它要尽快对外服务;勾它的一般是那些「晚点起也不影响开机」的服务。 「手动」这两年的含义变了。 以前手动就是「等人点」, 现在大量服务是「按需触发」——有程序调用它的接口,系统就把它拉起来,用完过一阵再关掉。 所以看到一堆手动服务在跑,是正常的。 把 SQL Server 设成「禁用」是个常见的坑。 有些人为了「省内存」把它禁掉,过几天忘了, 再连就报「无法连接到服务器」,而错误信息里不会告诉你「服务被禁用了」。

四、本机的实情:一个引擎都没有

演示里把整张服务注册表读了一遍。名字里带 SQL 的只有一条:.NET Data Provider for SqlServer。 它不是引擎,是驱动。 装在 .NET 里,给别的程序连 SQL Server 用的,自己不起数据库、不占端口、 也没有实例。看到它别以为「装过了」。 真正的引擎服务一定叫 MSSQLSERVER 或 MSSQL$<实例名>。 名字里带 SQL 的其它东西——驱动、 OLE DB 提供程序、Management Studio——都是客户端,不是服务端。 所以本章后面所有「启动服务」「注册服务器」的操作, 本沙箱只能演示判定逻辑与命令反馈,没有真实例可以让它启动。 涉及真实例才有输出的地方, 正文都会明确标出来。

五、动手 Demo:读一遍本机的服务注册表

这一段直接读 HKLM\SYSTEM\CurrentControlSet\Services,把 782 个服务的启动类型、运行账户数出来, 再看实例名怎么变成服务名,最后查本机到底有没有 SQL 引擎。

import winreg

KEY = r"SYSTEM\CurrentControlSet\Services"
k = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, KEY)
total = winreg.QueryInfoKey(k)[0]

START = {0: u"引导", 1: u"系统", 2: u"自动", 3: u"手动", 4: u"禁用"}
cnt, acct, sql = {}, {}, []
for i in range(total):
    name = winreg.EnumKey(k, i)
    sk = winreg.OpenKey(k, name)
    try:
        st = winreg.QueryValueEx(sk, u"Start")[0]
    except OSError:
        st = 3                       # 没写 Start 的按手动算
    cnt[st] = cnt.get(st, 0) + 1
    try:
        ob = winreg.QueryValueEx(sk, u"ObjectName")[0].lower()
    except OSError:
        ob = u""
    acct[ob] = acct.get(ob, 0) + 1
    if u"SQL" in name.upper():
        sql.append(name)
    sk.Close()

print(u"服务总数 %d" % total)
for st in sorted(cnt):
    print(u"  Start=%d %-4s %4d 个" % (st, START[st], cnt[st]))
print(u"名字里带 SQL 的:%s" % (sql or u"一个都没有"))

实跑输出:

--------------------------------------------------------------------
服务、实例、账户、启动类型
--------------------------------------------------------------------


  ① 本机一共装了 782 个服务
  --------------------------------------------------------------------
| Start 值 | 含义 | 本机个数 | 什么时候启动 |
|---|---|---|---|
| 0 | 引导 | 100 | 由系统加载器拉起,驱动层 |
| 1 | 系统 | 32 | 内核初始化时启动,服务层最底层 |
| 2 | 自动 | 93 | 开机后由服务控制管理器拉起 |
| 3 | 手动 | 517 | 有人调用才启动,也含「按需触发」 |
| 4 | 禁用 | 40 | 谁都起不来,连手动都不行 |

  这张表的值是刚读本机注册表数出来的,782 个服务的真实分布。
  注意「手动」最多——现代 Windows 大量服务改成按需触发,被系统事件叫起来,不是靠人点。

  ② 服务账户:决定这个服务能碰哪些文件
  --------------------------------------------------------------------
| 运行账户 | 服务个数 |
|---|---|
| (未设置,驱动类多) | 507 |
| LocalSystem 本机最高权限 | 186 |
| LocalService 本机低权限 | 65 |
| NetworkService 本机+网络 | 19 |
| 专用域账户 \Driver\WudfRd | 3 |

  SQL Server 安装时让你选的「服务账户」就是这一项。
  选 LocalSystem 最省事但权限过大;选 NetworkService 够用但访问网络共享会以机器身份出现;
  企业里通常给一个专用域账户,好处是备份目录、共享文件夹的授权能精确到它。

  ③ 实例名怎么变成服务名
  --------------------------------------------------------------------
| 实例名 | 服务名 | TCP 端口 | 备注 |
|---|---|---|---|
| (空)默认实例 | MSSQLSERVER | 1433 | 一台机器只能有一个 |
| SQLEXPRESS | MSSQL$SQLEXPRESS | 动态 | 装 Express 时默认给的名字 |
| SHOP | MSSQL$SHOP | 动态 | 多实例并存时自己起的名 |

  命名实例默认用动态端口,所以客户端连它要先问 SQL Browser 服务(UDP 1434)。
  SQL Browser 没启动,命名实例就连不上——这是新手最常见的「服务明明在跑却连不上」。

  ④ 本机的实际情况
  --------------------------------------------------------------------
  名字里带 SQL 的服务:1 个
    .NET Data Provider for SqlServer Start=3
  看着有一个,其实不是引擎——它是 .NET 里的数据提供程序,给别的程序连 SQL Server 用的驱动,
  不是数据库本身。
  真正的引擎服务一定叫 MSSQLSERVER 或 MSSQL$<实例名>,一个都没有就是没装。
  所以本章后面所有「启动服务」「注册服务器」的操作,本沙箱只能演示判定逻辑与命令反馈,
  没有真实例可以让它启动——凡是涉及真实例才有输出的地方,正文都会标出来。

第一段那张表是刚读出来的:782 个服务里手动 517、自动 93、禁用 40。 「手动」最多,这跟十年前完全不一样——现代 Windows 大量服务改成按需触发了。 第二段的账户分布里,「未设置」有 507 个。 这不是配错了,是驱动类服务没有用户态进程,自然没有登录身份。 真正要看的只有 LocalSystem、LocalService 和专用域账户这三档。 第三段那三行是这一节最该背下来的。 默认实例叫 MSSQLSERVER,命名实例叫 MSSQL$实例名; 默认实例固定用 1433,命名实例默认用动态端口,所以要先问 Browser 服务(UDP 1434)。 第四段查出来只有一条 .NET Data Provider for SqlServer。 它是驱动,不是引擎。这段代码的价值不在结果,在方法: 以后再有人问「这台机器装没装 SQL Server」,读一遍注册表就知道了,不用去翻安装记录。

30 秒实验

  1. 在你自己机器上跑一遍 net start,数一下一共有多少服务在跑。 再跟注册表里 Start=2 的个数比一比——两组数不会相等,想一想为什么。
  2. 打开服务面板,按「启动类型」排一下序,看看「自动」里有没有标着「延迟启动」的。 随便挑一个看属性,在「登录」页能看到它用什么账户跑。
  3. 想一想:如果让你给 SQL Server 选服务账户,LocalSystem 和专用域账户各有什么代价? LocalSystem 省事但权限过大;域账户最精确,但密码一改,服务就起不来了。

常见误区

  1. 「服务面板能看到 SQL Server,就说明装好了」。不一定。 SQL Server (MSSQLSERVER) 是引擎,SQL Server 代理 是配套, SQL Server Browser 只是个查端口的。 只有第一条在,才说明引擎装了。
  2. 「把所有服务都设成自动,省得每次手动起」。SQL Server 引擎设自动是对的, Browser 设自动也可以;但代理和全文搜索没必要,它们只在特定任务时才需要, 开机就起只是白占内存。
  3. 「服务账户随便选,反正是本地开发」。本地开发也建议用默认的虚拟账户。 用 LocalSystem 跑,等于让数据库进程拿到本机最高权限——一旦出注入类的漏洞, 攻击者顺着这条路就能碰系统文件。
自测三问(点开对答案) 1. 默认实例和命名实例,服务名各是什么格式? —— **默认实例:`MSSQLSERVER`;命名实例:`MSSQL$实例名`,中间是美元符号。** 服务面板里显示的是 `SQL Server (MSSQLSERVER)` 这种带括号的样子,但命令行要用括号里那个名字。 2. 为什么本机 507 个服务的账户是「未设置」? —— **因为它们是驱动**,跑在内核里,没有用户态进程,也就没有登录身份。 服务面板里看不到它们,注册表里能看到——这也说明「账户未设置」多数时候不是配置问题。 3. 把 SQL Server 设成「禁用」之后再连,会看到什么? —— **看到的是「无法连接到服务器」,不是「服务被禁用」。**客户端连不上只会报网络层或登录层的错, 它不知道服务端那条服务是禁用的。 所以排查顺序永远是:先看服务状态,再看端口,最后才怀疑连接串。

下一篇 3.2 讲「怎么把它起起来」。三种启动方式各有各的场合, 但真正有用的是那几条命令行反馈——服务名无效 和 错误 1068 各自意味着什么。

相关笔记