一个「实例」在 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 秒实验
- 在你自己机器上跑一遍
net start,数一下一共有多少服务在跑。 再跟注册表里 Start=2 的个数比一比——两组数不会相等,想一想为什么。 - 打开服务面板,按「启动类型」排一下序,看看「自动」里有没有标着「延迟启动」的。 随便挑一个看属性,在「登录」页能看到它用什么账户跑。
- 想一想:如果让你给 SQL Server 选服务账户,
LocalSystem和专用域账户各有什么代价? LocalSystem 省事但权限过大;域账户最精确,但密码一改,服务就起不来了。
常见误区
- 「服务面板能看到 SQL Server,就说明装好了」。不一定。
SQL Server (MSSQLSERVER)是引擎,SQL Server 代理是配套,SQL Server Browser只是个查端口的。 只有第一条在,才说明引擎装了。 - 「把所有服务都设成自动,省得每次手动起」。SQL Server 引擎设自动是对的, Browser 设自动也可以;但代理和全文搜索没必要,它们只在特定任务时才需要, 开机就起只是白占内存。
- 「服务账户随便选,反正是本地开发」。本地开发也建议用默认的虚拟账户。 用 LocalSystem 跑,等于让数据库进程拿到本机最高权限——一旦出注入类的漏洞, 攻击者顺着这条路就能碰系统文件。
自测三问(点开对答案)
1. 默认实例和命名实例,服务名各是什么格式? —— **默认实例:`MSSQLSERVER`;命名实例:`MSSQL$实例名`,中间是美元符号。** 服务面板里显示的是 `SQL Server (MSSQLSERVER)` 这种带括号的样子,但命令行要用括号里那个名字。 2. 为什么本机 507 个服务的账户是「未设置」? —— **因为它们是驱动**,跑在内核里,没有用户态进程,也就没有登录身份。 服务面板里看不到它们,注册表里能看到——这也说明「账户未设置」多数时候不是配置问题。 3. 把 SQL Server 设成「禁用」之后再连,会看到什么? —— **看到的是「无法连接到服务器」,不是「服务被禁用」。**客户端连不上只会报网络层或登录层的错, 它不知道服务端那条服务是禁用的。 所以排查顺序永远是:先看服务状态,再看端口,最后才怀疑连接串。下一篇 3.2 讲「怎么把它起起来」。三种启动方式各有各的场合,
但真正有用的是那几条命令行反馈——服务名无效 和 错误 1068 各自意味着什么。
- ☐ (本篇完,下一篇 3.2 启动 SQL Server 服务)