笔记17-徐 用户数据库常见问题和解决方法 附案例
1 --用户数据库常见问题和解决方法 附案例
2 --查询数据库的状态
3 SELECT state_desc FROM sys.databases
4
5 --1、文件打开问题 消息:database 'sales' cannot be opened due to inaccessible files or insufficient memory or disk space
6 --错误日志文件显示内容:
7 --一、主文件组:
8 --(1)访问主文件组的主文件出错
9 --starting up database 'sales'
10 --error:17207 ,severity:16 ,state:1
11 --FCB::OPEN operating system error2(cannot find the file specified) occured while creating or opening file
12 'C:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\Data\spridat.mdf'
13 --(2)访问主文件组的辅助文件出错
14 --starting up database 'sales'
15 --error :17207,severity:16 ,state:1
16 --FileMgr::StartPrimaryDataFiles operating system error2(cannot find the file specified) occured while creating or opening file
17 'C:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\Data\spridat.ndf'
18
19 --不管是上面哪一个情况,数据库都无法正常开启。只有将文件上的问题解决,数据库才能打开,如果问题不能解决,只能恢复数据库备份
20
21 --二、辅助文件组:
22 --starting up database 'sales'
23 --error :17207,severity:16 ,state:1
24 -- FileMgr::StartSecondaryDataFiles:operating system error2(cannot find the file specified) occured while creating or opening file
25 'C:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\Data\spridat2.ndf'
26
27 --和主文件组不同,一个数据库可以在辅助文件组OFFLINE的时候打开。我们可以标记辅助文件组OFFLINE,重新打开数据库
28 ALTER DATABASE sales MODIFY FILE(NAME=spridat2,OFFLINE)
29 ALTER DATABASE sales SET ONLINE
30
31 --这样打开的数据库能够看到主文件组和其他辅助文件组里的内容,但是offline的那个文件组内容不可见。
32 --这是你可以将数据导出到一个新数据库,或者用文件组备份恢复的方式恢复那个出问题的文件组
33
34 --三、日志文件
35 --简单模式:当日志文件不可访问的时候,如果数据库的恢复模式为简单恢复模式,上次数据库关闭正常(所有该提交的事务都已经写入硬盘
36 --,所有该回滚的事务都已经撤销),那么在下次SQL启动这个数据库的时候,如果发现日志文件不存在,则会自动创建一个新的
37 --日志文件。这也是为什麽有时候日志文件被删除后,数据库照样能启动的原因。
38
39 --完整模式:如果上次数据库没有正常关闭,或者恢复模式是完整,SQL就不会给这个数据库创建日志文件。
40
41 --错误日志文件显示:
42 --starting up database 'sales'
43 --error :17207,severity:16 ,state:1
44 -- FileMgr::StartLogFiles:operating system error2(cannot find the file specified) occured while creating or opening file
45 --'C:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\Data\salelog.ldf'
46 --the log cannot be rebuilt because the database was not cleanly shut down
47
48 --如果我们不能修复日志上的错误,那么最好的方法是恢复数据库备份。
49 --因为这时候数据文件里的数据正处在一个不一致的状态。可能有些事务已经提交了,但是数据在日志文件里
50 --也有一些事务已经撤销了,但数据文件里的数据还是撤销前的状态。如果这些信息找不回来,就会发生事务没有
51 --完全提交或回滚的现象。
52
53 --解决日志文件错误办法:
54 --checkdb:后果是有数据丢失
55 --(1)SQL2000:DBCC REBUILD_LOG 跟SQL2005效果差不多
56 --(2)SQL2005:如下
57 ALTER DATABASE sales SET EMERGENCY
58 GO
59 ALTER DATABASE sales SET SINGLE_USER
60 GO
61 DBCC CHECKDB(sales,REPAIR_ALLOW_DATA_LOSS)
62 GO
63 ALTER DATABASE sales SET MULTI_USER
64 GO
65
66
67
68
69 --2、恢复失败
70 --数据库进入恢复后,要做重做(roll forward) 和撤销(roll back)的工作中间可能会遇到3类问题
71 --(1)在重做过程中遇到的能延迟的错误(deferrable error)
72 --(2)在撤销的过程中遇到能延迟的错误
73 --(3)在任何时间遇到的不能延迟的错误
74
75 --(1)重做遇到的错误
76 --举到的例子:由于1:153上的数据没有被正常重做,所以这个页面是不能正常访问的。SQL会标记这个页面为RestorePending
77 --任何操作想要访问这个页面,SQL都会抛出下面错误
78 --Database ID 5 ,PAGE(1:153)is marked RestorePending
79 --如果想要修复上面的错误,必须运行dbcc checkdb 或者使用数据库备份进行恢复
80
81
82 --(2)撤销过程中的失败
83 --举到的例子:从这些信息可以看到,SQL在页面1:153上面发生访问错误,以至于日志(51:104:5)所代表
84 --的那个事务不能正常回滚。SQL标记这个事务(不光是这个页面)为延迟事务,然后继续其他撤销工作。
85 --最后数据库正常上线。但是由于有事务没有被正常回滚,所以在和这个事务有关的那些对象上,SQL
86 --申请了一些锁,以防止问题解决之前有其他用户访问这些对象。锁的主人编号是-3。
87 --你可以运行下面代码来检查SQL有没有这样的锁
88 SELECT
89 CONVERT(CHAR(22),resource_description)AS resource_desc,
90 CONVERT(CHAR(15),resource_type) AS resource_type,
91 request_session_id,
92 CONVERT(CHAR(15),request_mode) AS request_mode,
93 CONVERT(CHAR(15),request_status) AS request_status
94 from sys.dm_tran_locks
95 WHERE request_session_id=-3
96
97 --和RestorePending的页面一样,要修复页面,最好的方式是备份恢复。因为这样能保证数据库里
98 --的数据都是事务一致的,而且也对数据的丢失量心中有数。
99 --如果没有备份,只能DBCC CHECKDB或者DBCC CHECKTABLE ,修复物理损坏,但是逻辑错误就无能为力了
100 --因为管理员也不知道到底哪些数据出了逻辑问题
101 ALTER DATABASE sales SET EMERGENCY
102 GO
103 ALTER DATABASE sales SET SINGLE_USER
104 GO
105 DBCC CHECKDB(sales,REPAIR_ALLOW_DATA_LOSS)
106 GO
107 ALTER DATABASE sales SET MULTI_USER
108 GO
109 -------------------------------------------
110 --DBCC CHECKTABLE()
111 dbcc checktable('需要修复的数据表的名称',REPAIR_ALLOW_DATA_LOSS)
112 dbcc checktable('需要修复的数据表的名称',REPAIR_REBUILD)
113
114
115 --(3)可疑模式
116 --当数据库做恢复的时候遇到的错误很严重,无法延迟错误时,整个数据库会进入可疑模式。
117 --这时整个数据库都无法访问。发生这种情况,
118 --1要不就是数据库损坏范围太大,
119 --2要不就是一些比较重要的数据库分配页出现了问题。
120 --3如果因为是硬件的问题,可以在硬件修复后,重启SQL,SQL会对每个数据库都做恢复,哪怕这个数据库上次被标记为可疑。
121
122
123 --可疑模式解决办法:
124 --(1)恢复备份
125 --(2)先移走数据库日志文件,重启SQL,数据库会进入RECOVERY_PENDING状态,然后
126 ALTER DATABASE sales SET EMERGENCY
127 GO
128 ALTER DATABASE sales SET SINGLE_USER
129 GO
130 DBCC CHECKDB(sales,REPAIR_ALLOW_DATA_LOSS)
131 GO
132 ALTER DATABASE sales SET MULTI_USER
133 GO
134 --SQL会创建一个新的日志文件。由于这个日志文件是空的,数据库无须做任何重做和撤销的工作,
135 --也就不容易进入可疑模式。数据库可以被打开,但是很多有问题的对象还是不能访问,只能将它们
136 --导入到新建的数据库里面,能挽救多少是多少。
137 --(3)只有符号要求的备份才能挽救数据
138
139
140
141
142 --------------------------------------------------------------------------------
143 --案例
144 -----------------------------------------------------------------------------------
145 --数据库恢复模式为 完整 ,先前做过一次完整备份。原先的表格里所有的值都是AAAA...
146 --上次SQL关闭的时候,有个将100条记录修改成BBBB....的事务没来得及提交。
147 UPDATE test SET col2=REPLICATE('B',300) WHERE col1<100
148
149 --现在它启动的时候,由于数据文件发生损坏,在撤销时遇到1:200页面上的824错误,
150 --相关事务被标记为延迟的事务
151 --数据库能正常打开,运行下面的查询会有许多被-3持有的锁,不但在key上,还在很多page上
152
153 --查询SQL现有的锁
154 USE pagenumber
155 GO
156 SELECT
157 p.object_id,
158 OBJECT_NAME(p.object_id) AS OBJECT_NAME,
159 request_session_id,
160 resource_type,
161 resource_description,
162 request_mode,
163 resource_associated_entity_id,
164 request_status
165 FROM sys.dm_tran_locks AS lock LEFT JOIN sys.partitions p
166 ON lock.resource_associated_entity_id=p.hobt_id
167 WHERE resource_database_id=DB_ID('pagenumber')
168 ORDER BY request_session_id,request_mode,resource_type,resource_associated_entity_id
169 GO
170
171 --如果运行下面的查询,就会被阻塞住
172 SELECT * FROM pagenumber..test
173
174 --查询sys.sysprocesses视图,就能够看到连接被-3阻塞住了
175 SELECT * FROM sys.sysprocesses WHERE spid>50
176
177 --两个方式修复数据库
178 --(1)DBCC CHECKDB
179 ALTER DATABASE pagenumber SET EMERGENCY
180 GO
181 ALTER DATABASE pagenumber SET SINGLE_USER
182 GO
183 DBCC CHECKDB(pagenumber,REPAIR_ALLOW_DATA_LOSS)
184 GO
185 ALTER DATABASE pagenumber SET MULTI_USER
186 GO
187
188 --结果:错误的结果 只撤销了部分记录修改
189
190 --(2)由于数据库是完整恢复模式,先前又做过完整备份,所以可以用页面恢复的方式,很快恢复数据库
191 --到一个一致的状态
192 --首先需要找到所有有问题的页面,查看errorlog,或者数据库不大的话可以在紧急模式运行不带参数的
193 --checkdb,把所有错误页面打出来
194
195 ALTER DATABASE pagenumber SET EMERGENCY
196 GO
197 DBCC CHECKDB('pagenumber')
198 GO
199 ALTER DATABASE pagenumber SET ONLINE
200 GO
201 --现在我们知道是1:200这个页面有问题,就只需恢复这个页面就可以了
202 USE master
203 GO
204 RESTORE DATABASE pagenumber PAGE='1:200'
205 FROM DISK='pagenumber.bak'
206
207
208 --这时候查询还是不能正常运行,因为那个事务还没有回滚。现在我们做一次尾日志备份
209 BACKUP LOG pagenumber TO DISK='pagenumbergood.trn' WITH init,FORMAT
210 GO
211
212 --然后我们把日志备份依次恢复。这里假设只有刚才做的一份
213 RESTORE LOG pagenumber FROM DISK='pagenumbergood.trn' WITH recovery
214 GO
215 --现在再做查询可以发现数据是一致的
216
217
218 --用checkdb命令恢复处理的数据虽然在物理上恢复了正常,但是一些可能已经是错误的了。
219 --所以管理员要少用checkdb+repair_allow_data_loss,尽可能使用备份恢复的方法