|
|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
引言
SQL Server作为微软推出的关系型数据库管理系统,广泛应用于企业级应用中。在SQL Server的运行过程中,事务日志(Transaction Log)扮演着至关重要的角色,它记录了所有对数据库的修改操作,是数据恢复和事务一致性的基石。然而,在实际运维中,许多数据库管理员都曾遭遇过日志文件突然暴涨的问题,这不仅会占用大量磁盘空间,还可能导致数据库性能下降,甚至影响业务连续性。
本文将深入探讨SQL Server数据库日志暴涨的根本原因,分析其潜在危害,并提供一系列实用的诊断、解决方案和预防措施,帮助数据库管理员有效应对这一常见挑战,确保数据库系统的稳定运行和业务的连续性。
SQL Server事务日志基础
事务日志的作用
事务日志是SQL Server数据库的核心组件之一,其主要功能包括:
1. 事务恢复:在系统崩溃或意外关机后,SQL Server利用事务日志进行前滚(Rollforward)和回滚(Rollback)操作,确保数据一致性。
2. 事务一致性:保证ACID(原子性、一致性、隔离性、持久性)特性中的原子性和持久性。
3. 数据恢复:支持将数据库恢复到特定时间点。
4. 复制与高可用性:在数据库镜像、AlwaysOn可用性组等技术中传输事务。
事务日志的结构
事务日志在物理上是由一个或多个日志文件组成的,逻辑上则由日志记录序列(Log Sequence Number, LSN)构成。每个日志记录都有一个唯一的LSN,并记录了事务执行的详细信息。
事务日志文件在内部被划分为虚拟日志文件(Virtual Log Files, VLFs),VLF是事务日志管理的最小单位。过多的VLFs会影响日志性能,因此合理配置日志初始大小和增长策略非常重要。
事务日志的工作原理
当执行数据修改操作时,SQL Server会经历以下过程:
1. 将数据页从磁盘读入缓冲区缓存(Buffer Cache)
2. 在缓冲区缓存中修改数据页
3. 将修改操作记录到事务日志中
4. 将事务日志记录写入磁盘(日志写入是同步的,确保持久性)
5. 数据页在检查点(Checkpoint)或惰性写入器(Lazy Writer)的作用下被写入磁盘
这种”Write-Ahead Logging”机制确保了即使在系统故障情况下,已提交的事务不会丢失,未提交的事务可以被回滚。
日志暴涨的常见原因分析
1. 数据库恢复模式设置不当
SQL Server提供了三种恢复模式,每种模式对日志的处理方式不同:
• 简单恢复模式(Simple Recovery Model):事务日志在检查点后自动截断,不支持时间点恢复。
• 完整恢复模式(Full Recovery Model):事务日志会记录所有操作,直到日志备份,支持时间点恢复。
• 大容量日志恢复模式(Bulk-Logged Recovery Model):对大容量操作进行最小日志记录,减少日志增长。
如果将生产数据库设置为完整恢复模式但未定期执行日志备份,事务日志会持续增长直至填满磁盘空间。
- -- 查看数据库恢复模式
- SELECT name, recovery_model_desc
- FROM sys.databases
- WHERE name = 'YourDatabaseName';
- -- 修改数据库恢复模式
- ALTER DATABASE YourDatabaseName SET RECOVERY SIMPLE;
- -- 或
- ALTER DATABASE YourDatabaseName SET RECOVERY FULL;
复制代码
2. 长时间运行的事务
长时间运行的事务会阻止日志截断,即使已执行日志备份。这是因为事务日志需要记录所有活动的、未完成的事务,直到它们提交或回滚。
常见长时间运行的事务包括:
• 未提交的显式事务
• 大规模数据导入/导出操作
• 缺乏优化的长时间运行的查询
- -- 查找长时间运行的事务
- SELECT
- DB_NAME(database_id) AS DatabaseName,
- session_id,
- start_time,
- status,
- command,
- transaction_id,
- last_request_start_time,
- last_request_end_time
- FROM sys.dm_tran_session_transactions AS t
- JOIN sys.dm_exec_sessions AS s
- ON t.session_id = s.session_id
- JOIN sys.dm_exec_requests AS r
- ON t.session_id = r.session_id
- WHERE DATEDIFF(MINUTE, start_time, GETDATE()) > 30; -- 运行超过30分钟的事务
复制代码
3. 高频大批量数据操作
执行大量数据修改操作,如大批量插入、更新或删除,会生成大量日志记录。特别是在完整恢复模式下,这些操作会显著增加日志大小。
例如,以下操作可能导致日志快速增长:
- -- 大批量插入
- INSERT INTO LargeTable (Column1, Column2, ...)
- SELECT Column1, Column2, ...
- FROM AnotherTable
- WHERE SomeCondition;
- -- 大批量更新
- UPDATE LargeTable
- SET Column1 = 'NewValue'
- WHERE SomeCondition;
- -- 大批量删除
- DELETE FROM LargeTable
- WHERE SomeCondition;
复制代码
4. 索引维护操作
索引重建或重组操作会产生大量事务日志,特别是在大型表上执行时。
- -- 索引重建会产生大量日志
- ALTER INDEX IX_IndexName ON TableName REBUILD;
- -- 索引重组产生的日志较少
- ALTER INDEX IX_IndexName ON TableName REORGANIZE;
复制代码
5. 复制或可用性组延迟
如果数据库参与了事务复制或AlwaysOn可用性组,日志可能因为复制或同步延迟而无法截断。当辅助副本无法及时应用事务时,主数据库的事务日志会持续增长。
- -- 检查数据库镜像状态
- SELECT database_id,
- mirroring_state_desc,
- mirroring_role_desc
- FROM sys.database_mirroring;
- -- 检查AlwaysOn可用性组状态
- SELECT
- ag.name AS AvailabilityGroupName,
- db.name AS DatabaseName,
- drs.is_local,
- drs.is_primary_replica,
- drs.synchronization_state_desc,
- drs.synchronization_health_desc
- FROM sys.dm_hadr_database_replica_states drs
- JOIN sys.availability_groups ag ON drs.group_id = ag.group_id
- JOIN sys.databases db ON drs.database_id = db.database_id;
复制代码
6. 磁盘空间不足或自动增长配置不当
如果事务日志文件的自动增长设置不当,或者磁盘空间不足,可能导致日志文件无法正常增长,从而引发问题。
- -- 查看日志文件的自动增长配置
- SELECT
- name AS FileName,
- size/128.0 AS CurrentSizeMB,
- growth/128.0 AS GrowthMB,
- CASE is_percent_growth
- WHEN 1 THEN 'Percentage'
- ELSE 'MB'
- END AS GrowthUnit
- FROM sys.database_files
- WHERE type = 1; -- 1 = 日志文件
复制代码
日志暴涨的危害
1. 磁盘空间耗尽
最直接的影响是日志文件占用大量磁盘空间,可能导致磁盘空间耗尽,进而影响SQL Server甚至整个操作系统的正常运行。
2. 数据库性能下降
过大的日志文件会导致以下性能问题:
• 日志写入操作变慢,因为需要管理更大的文件
• 检查点操作需要更长时间完成
• 数据库启动和恢复时间延长
• 备份操作耗时增加
3. 业务中断
在严重情况下,日志暴涨可能导致数据库变为只读模式或完全不可用,造成业务中断。SQL Server在日志空间不足时会采取以下措施:
• 对于简单恢复模式的数据库,尝试自动截断日志
• 对于完整恢复模式的数据库,如果日志已满且无法截断,数据库将变为只读模式,直到日志空间被释放
4. 恢复时间延长
过大的事务日志意味着在数据库恢复时需要处理更多日志记录,延长了系统故障后的恢复时间,影响业务连续性。
诊断与监控方法
1. 监控日志空间使用情况
定期监控事务日志的使用情况,可以及早发现潜在问题。
- -- 查看日志空间使用情况
- DBCC SQLPERF(LOGSPACE);
- -- 查看特定数据库的日志使用详情
- SELECT
- DB_NAME(database_id) AS DatabaseName,
- COUNT(*) AS VLFCount,
- SUM(CASE WHEN status = 0 THEN size END) * 8.0 / 1024 AS TotalSizeMB,
- SUM(CASE WHEN status = 2 THEN size END) * 8.0 / 1024 AS ActiveSizeMB
- FROM sys.dm_db_log_info(DB_ID())
- GROUP BY database_id;
复制代码
2. 检查日志截断状态
了解日志为何无法截断是解决问题的关键。
- -- 检查日志截断状态
- SELECT
- name AS DatabaseName,
- log_reuse_wait_desc
- FROM sys.databases
- WHERE log_reuse_wait_desc <> 'NOTHING';
复制代码
log_reuse_wait_desc的可能值及其含义:
• NOTHING:日志可以立即截断
• CHECKPOINT:等待检查点
• LOG_BACKUP:等待日志备份
• ACTIVE_BACKUP_OR_RESTORE:备份或还原操作正在进行
• ACTIVE_TRANSACTION:有活动事务
• DATABASE_MIRRORING:数据库镜像正在同步
• REPLICATION:事务复制正在处理
• DATABASE_SNAPSHOT_CREATION:正在创建数据库快照
• LOG_SCAN:日志扫描正在进行
• AVAILABILITY_REPLICA:AlwaysOn可用性组同步
• OTHER_TRANSIENT:临时原因
3. 分析事务日志内容
使用fn_dblog函数可以分析事务日志内容,找出占用空间最多的操作。
- -- 分析事务日志内容
- SELECT
- Operation,
- COUNT(*) AS OperationCount,
- SUM([Log Record Length]) AS TotalBytes
- FROM fn_dblog(NULL, NULL)
- GROUP BY Operation
- ORDER BY TotalBytes DESC;
- -- 查找特定事务
- SELECT
- [Current LSN],
- Operation,
- Context,
- [Transaction ID],
- [Begin Time],
- [End Time],
- [Transaction Name],
- [Transaction SID]
- FROM fn_dblog(NULL, NULL)
- WHERE [Transaction ID] = '0000:00000000'; -- 替换为实际的事务ID
复制代码
4. 设置警报和自动化监控
通过SQL Server Agent或第三方监控工具设置警报,当日志使用率达到阈值时自动通知管理员。
- -- 创建日志空间监控作业
- USE msdb;
- GO
- EXEC dbo.sp_add_job
- @job_name = N'Monitor Transaction Log Space';
- EXEC sp_add_jobstep
- @job_name = N'Monitor Transaction Log Space',
- @step_name = N'Check Log Space',
- @subsystem = N'TSQL',
- @command = N'
- DECLARE @LogSpaceUsed TABLE (
- DatabaseName VARCHAR(255),
- LogSizeMB DECIMAL(10,2),
- LogSpaceUsedPercent DECIMAL(10,2),
- Status INT
- );
- INSERT INTO @LogSpaceUsed
- EXEC(''DBCC SQLPERF(LOGSPACE)'');
- SELECT DatabaseName, LogSpaceUsedPercent
- FROM @LogSpaceUsed
- WHERE LogSpaceUsedPercent > 80; -- 超过80%使用率
- ',
- @database_name = N'master';
- EXEC sp_add_jobserver
- @job_name = N'Monitor Transaction Log Space';
- -- 创建警报
- EXEC dbo.sp_add_alert
- @name = N'Transaction Log Space Alert',
- @message_id = 0,
- @severity = 0,
- @enabled = 1,
- @delay_between_responses = 60,
- @include_event_description_in = 1,
- @job_name = N'Monitor Transaction Log Space',
- @category_name = N'[Uncategorized]';
复制代码
解决方案与应对策略
1. 紧急处理措施
当日志暴涨导致数据库空间危机时,需要立即采取措施:
如果磁盘空间允许,可以手动扩大日志文件:
- -- 增加日志文件大小
- ALTER DATABASE YourDatabaseName
- MODIFY FILE
- (
- NAME = YourDatabaseName_log,
- SIZE = 1024MB -- 设置为合适的大小
- );
复制代码
如果当前磁盘空间不足,可以在其他磁盘上添加新的日志文件:
- -- 添加新的日志文件
- ALTER DATABASE YourDatabaseName
- ADD LOG FILE
- (
- NAME = YourDatabaseName_log2,
- FILENAME = 'D:\SQLLogs\YourDatabaseName_log2.ldf',
- SIZE = 512MB,
- MAXSIZE = 2048MB,
- FILEGROWTH = 256MB
- );
复制代码
对于非关键生产环境,可以临时切换到简单恢复模式以截断日志:
- -- 切换到简单恢复模式
- ALTER DATABASE YourDatabaseName SET RECOVERY SIMPLE;
- -- 收缩日志文件
- DBCC SHRINKFILE(YourDatabaseName_log, 1024); -- 收缩到1024MB
- -- 记得在问题解决后切换回完整恢复模式
- ALTER DATABASE YourDatabaseName SET RECOVERY FULL;
- -- 并立即执行完整备份
- BACKUP DATABASE YourDatabaseName TO DISK = 'C:\Backup\YourDatabaseName_full.bak';
复制代码
2. 长期解决方案
对于完整恢复模式的数据库,定期执行日志备份是防止日志暴涨的关键:
- -- 执行日志备份
- BACKUP LOG YourDatabaseName TO DISK = 'C:\Backup\YourDatabaseName_log.trn';
- -- 创建日志备份的维护计划
- USE msdb;
- GO
- EXEC dbo.sp_add_job
- @job_name = N'Backup Transaction Log';
- EXEC sp_add_jobstep
- @job_name = N'Backup Transaction Log',
- @step_name = N'Backup Log',
- @subsystem = N'TSQL',
- @command = N'
- BACKUP LOG YourDatabaseName
- TO DISK = ''C:\Backup\YourDatabaseName_log_'' + REPLACE(CONVERT(VARCHAR, GETDATE(), 120), '':'', ''-'') + ''.trn''
- WITH COMPRESSION;
- ',
- @database_name = N'master';
- EXEC sp_add_jobschedule
- @job_name = N'Backup Transaction Log',
- @name = N'Every 15 minutes',
- @freq_type = 4, -- daily
- @freq_interval = 1,
- @freq_subday_type = 4, -- minutes
- @freq_subday_interval = 15;
- EXEC sp_add_jobserver
- @job_name = N'Backup Transaction Log';
复制代码
识别并优化长时间运行的事务:
- -- 查找并终止长时间运行的事务(谨慎使用)
- SELECT
- DB_NAME(database_id) AS DatabaseName,
- session_id,
- start_time,
- status,
- command,
- transaction_id,
- last_request_start_time,
- last_request_end_time
- FROM sys.dm_tran_session_transactions AS t
- JOIN sys.dm_exec_sessions AS s
- ON t.session_id = s.session_id
- JOIN sys.dm_exec_requests AS r
- ON t.session_id = r.session_id
- WHERE DATEDIFF(MINUTE, start_time, GETDATE()) > 30; -- 运行超过30分钟的事务
- -- 终止特定会话(谨慎使用)
- -- KILL 51; -- 替换为实际的session_id
复制代码
对于大批量数据操作,可以采用以下策略减少日志生成:
- -- 使用批量插入的最小日志记录方法(需要满足特定条件)
- -- 1. 数据库使用完整或大容量日志恢复模式
- -- 2. 目标表为空或没有索引
- -- 3. 使用TABLOCK提示
- -- 示例:批量插入的最小日志记录
- BULK INSERT LargeTable
- FROM 'C:\Data\LargeDataFile.csv'
- WITH (
- TABLOCK,
- BATCHSIZE = 10000,
- ERRORFILE = 'C:\Data\Errors.csv'
- );
- -- 另一种方法:分批处理大型操作
- DECLARE @BatchSize INT = 10000;
- DECLARE @MaxID INT, @CurrentID INT = 0;
- SELECT @MaxID = MAX(ID) FROM SourceTable;
- WHILE @CurrentID < @MaxID
- BEGIN
- DELETE FROM LargeTable
- WHERE ID BETWEEN @CurrentID AND @CurrentID + @BatchSize - 1;
-
- SET @CurrentID = @CurrentID + @BatchSize;
-
- -- 每批操作后执行日志备份(如果使用完整恢复模式)
- BACKUP LOG YourDatabaseName TO DISK = 'C:\Backup\YourDatabaseName_log.trn';
-
- -- 短暂等待以减少系统负载
- WAITFOR DELAY '00:00:01';
- END
复制代码
优化索引维护操作以减少日志生成:
- -- 使用ONLINE选项减少锁定和日志生成
- ALTER INDEX IX_IndexName ON TableName REBUILD
- WITH (ONLINE = ON, SORT_IN_TEMPDB = ON);
- -- 分批重建大型表的索引
- DECLARE @BatchSize INT = 10000;
- DECLARE @MaxID INT, @CurrentID INT = 0;
- SELECT @MaxID = MAX(ID) FROM LargeTable;
- WHILE @CurrentID < @MaxID
- BEGIN
- -- 创建临时表存储当前批次的ID
- CREATE TABLE #BatchIDs (ID INT PRIMARY KEY);
-
- -- 插入当前批次的ID
- INSERT INTO #BatchIDs
- SELECT ID FROM LargeTable
- WHERE ID BETWEEN @CurrentID AND @CurrentID + @BatchSize - 1;
-
- -- 仅处理当前批次的数据
- -- 这里可以执行特定的索引维护操作
-
- -- 删除临时表
- DROP TABLE #BatchIDs;
-
- SET @CurrentID = @CurrentID + @BatchSize;
-
- -- 短暂等待以减少系统负载
- WAITFOR DELAY '00:00:01';
- END
复制代码
合理配置日志文件的初始大小和增长参数:
- -- 查看当前日志文件配置
- SELECT
- name AS FileName,
- size/128.0 AS CurrentSizeMB,
- growth/128.0 AS GrowthMB,
- CASE is_percent_growth
- WHEN 1 THEN 'Percentage'
- ELSE 'MB'
- END AS GrowthUnit
- FROM sys.database_files
- WHERE type = 1; -- 1 = 日志文件
- -- 优化日志文件配置
- ALTER DATABASE YourDatabaseName
- MODIFY FILE
- (
- NAME = YourDatabaseName_log,
- SIZE = 1024MB, -- 设置合理的初始大小
- FILEGROWTH = 256MB -- 设置固定增长量而非百分比
- );
复制代码
预防措施
1. 合理规划日志文件大小
根据数据库的工作负载和备份策略,合理规划日志文件的初始大小:
• 对于高事务量的数据库,设置较大的初始日志文件大小
• 避免频繁的小额自动增长,这会导致过多的VLFs
• 定期监控VLF数量,必要时重新创建日志文件
- -- 检查VLF数量
- DBCC LOGINFO;
- -- 如果VLF过多(如超过1000),考虑重新创建日志文件
- -- 1. 备份数据库
- BACKUP DATABASE YourDatabaseName TO DISK = 'C:\Backup\YourDatabaseName_full.bak';
- -- 2. 备份事务日志
- BACKUP LOG YourDatabaseName TO DISK = 'C:\Backup\YourDatabaseName_log.trn';
- -- 3. 收缩日志文件到最小
- DBCC SHRINKFILE(YourDatabaseName_log, 1);
- -- 4. 重新设置日志文件为合适大小
- ALTER DATABASE YourDatabaseName
- MODIFY FILE
- (
- NAME = YourDatabaseName_log,
- SIZE = 1024MB,
- FILEGROWTH = 256MB
- );
复制代码
2. 实施有效的备份策略
根据业务需求制定合理的备份策略:
• 对于关键业务数据库,使用完整恢复模式并定期执行日志备份
• 对于非关键数据库,考虑使用简单恢复模式
• 使用Ola Hallengren的维护解决方案或类似工具自动化备份过程
- -- 使用Ola Hallengren的存储过程执行日志备份
- EXECUTE dbo.DatabaseBackup
- @Databases = 'USER_DATABASES',
- @Directory = 'C:\Backup',
- @BackupType = 'LOG',
- @Verify = 'Y',
- @Compress = 'Y',
- @CheckSum = 'Y',
- @LogToTable = 'Y';
复制代码
3. 监控与预警
建立完善的监控体系,及早发现潜在问题:
• 设置日志空间使用率警报
• 监控长时间运行的事务
• 跟踪备份作业的执行情况
- -- 创建日志空间监控存储过程
- CREATE PROCEDURE dbo.usp_MonitorTransactionLogSpace
- AS
- BEGIN
- DECLARE @Threshold INT = 80; -- 80%阈值
- DECLARE @Table TABLE (
- DatabaseName VARCHAR(255),
- LogSizeMB DECIMAL(10,2),
- LogSpaceUsedPercent DECIMAL(10,2),
- Status INT
- );
-
- INSERT INTO @Table
- EXEC('DBCC SQLPERF(LOGSPACE)');
-
- SELECT DatabaseName, LogSpaceUsedPercent
- FROM @Table
- WHERE LogSpaceUsedPercent > @Threshold;
-
- -- 如果有数据库超过阈值,发送警报
- IF EXISTS (SELECT 1 FROM @Table WHERE LogSpaceUsedPercent > @Threshold)
- BEGIN
- DECLARE @Body NVARCHAR(MAX);
- SET @Body = '以下数据库的事务日志空间使用率超过' + CAST(@Threshold AS VARCHAR) + '%:<br><br>';
-
- SELECT @Body = @Body + DatabaseName + ': ' + CAST(LogSpaceUsedPercent AS VARCHAR) + '%<br>'
- FROM @Table
- WHERE LogSpaceUsedPercent > @Threshold;
-
- -- 发送邮件警报(需要配置数据库邮件)
- EXEC msdb.dbo.sp_send_dbmail
- @profile_name = 'DBA_Profile',
- @recipients = 'dba@company.com',
- @subject = '警报:数据库事务日志空间使用率过高',
- @body = @Body,
- @body_format = 'HTML';
- END
- END;
复制代码
4. 定期维护与优化
定期进行数据库维护,保持数据库性能:
• 定期更新统计信息
• 重建或重组碎片化索引
• 清理历史数据
- -- 使用Ola Hallengren的维护解决方案进行索引优化
- EXECUTE dbo.IndexOptimize
- @Databases = 'USER_DATABASES',
- @FragmentationLow = 'INDEX_REORGANIZE',
- @FragmentationMedium = 'INDEX_REORGANIZE',
- @FragmentationHigh = 'INDEX_REBUILD',
- @LogToTable = 'Y';
复制代码
最佳实践与建议
1. 根据业务需求选择合适的恢复模式
• 关键业务数据库:使用完整恢复模式,定期执行完整备份和事务日志备份
• 报表或只读数据库:考虑使用简单恢复模式
• 数据仓库或批量处理数据库:考虑使用大容量日志恢复模式,在大容量操作期间减少日志生成
2. 合理配置日志文件
• 根据数据库事务量设置适当的初始日志文件大小
• 使用固定增长量而非百分比增长
• 将日志文件放在专用的高速磁盘上
• 考虑使用多个日志文件,分布在不同的物理磁盘上
3. 优化大批量操作
• 将大批量操作分解为多个小批次
• 在非高峰期执行大批量操作
• 考虑使用最小日志记录方法(如BULK INSERT with TABLOCK)
• 在大批量操作前后执行日志备份
4. 建立完善的监控体系
• 监控日志空间使用情况
• 跟踪长时间运行的事务
• 设置合理的警报阈值
• 定期检查VLF数量
5. 制定应急响应计划
• 为日志空间不足的情况制定应急响应流程
• 准备好必要的脚本和工具
• 确保团队成员了解应急处理步骤
• 定期进行应急演练
结论
SQL Server数据库日志暴涨是一个常见但严重的问题,可能导致数据库性能下降、磁盘空间耗尽甚至业务中断。通过深入理解事务日志的工作原理、识别日志暴涨的根本原因,并采取适当的预防和应对措施,数据库管理员可以有效管理日志空间,确保数据库系统的稳定运行和业务的连续性。
关键在于建立完善的监控体系、实施合理的备份策略、优化数据库操作,并根据业务需求选择合适的恢复模式。同时,定期进行数据库维护和优化,制定应急响应计划,也是确保数据库健康运行的重要环节。
通过本文提供的策略和方法,数据库管理员可以更好地应对SQL Server数据库日志暴涨问题,轻松解决数据库空间危机,确保业务的连续性和稳定性。记住,预防胜于治疗,持续的监控和管理是避免日志空间危机的最佳途径。
版权声明
1、转载或引用本网站内容(SQL Server数据库日志暴涨问题深度解析与有效应对策略助您轻松解决数据库空间危机确保业务连续性)须注明原网址及作者(威震华夏关云长),并标明本网站网址(https://pixtech.cc/)。
2、对于不当转载或引用本网站内容而引起的民事纷争、行政处理或其他损失,本网站不承担责任。
3、对不遵守本声明或其他违法、恶意使用本网站内容者,本网站保留追究其法律责任的权利。
本文地址: https://pixtech.cc/thread-40572-1-1.html
|
|