| | |
| | | using NPOI.HSSF.Record; |
| | | //using Microsoft.Data.SqlClient; |
| | | //using NPOI.HSSF.Record; |
| | | //using SkiaSharp; |
| | | //using System; |
| | | //using System.Collections.Generic; |
| | | //using System.Linq; |
| | | //using System.Linq.Expressions; |
| | | //using System.Text; |
| | | //using System.Threading.Tasks; |
| | | //using WIDESEAWCS_Core.BaseServices; |
| | | //using WIDESEAWCS_ISystemRepository; |
| | | //using WIDESEAWCS_ISystemServices; |
| | | //using WIDESEAWCS_Model.Models; |
| | | |
| | | //namespace WIDESEAWCS_SystemServices |
| | | //{ |
| | | // public class Sys_LogService : ServiceBase<Sys_Log, ISys_LogRepository>, ISys_LogService |
| | | // { |
| | | // private static DateTime? _lastExecutionDate = null; |
| | | // private readonly object _lock = new object(); |
| | | // private const string DbName = "WIDESEA_HF"; |
| | | // private const long MaxMdfSizeBytes = 8L * 1024 * 1024 * 1024; // 8GB |
| | | // private const int TargetShrinkSizeMB = 4500; // 4.5GB |
| | | |
| | | // public Sys_LogService(ISys_LogRepository BaseDal) : base(BaseDal) |
| | | // { |
| | | |
| | | // } |
| | | |
| | | // public void DeleteOldLogs() |
| | | // { |
| | | // // 使ç¨éç¡®ä¿çº¿ç¨å®å
¨ |
| | | // lock (_lock) |
| | | // { |
| | | // var today = DateTime.Today; |
| | | |
| | | // // æ£æ¥ä»å¤©æ¯å¦å·²ç»æ§è¡è¿ |
| | | // if (_lastExecutionDate.HasValue && _lastExecutionDate.Value.Date == today) |
| | | // { |
| | | // return; |
| | | // } |
| | | // try |
| | | // { |
| | | // // 计ç®3个æåçæ¥æ |
| | | // var threeMonthsAgo = DateTime.Now.AddMonths(-3); |
| | | // // 计ç®3天åçæ¥æ |
| | | // var sevenDaysAgo = DateTime.Now.AddDays(-3); |
| | | // // ç¹å®URL |
| | | // var specificUrl = "http://11.2.30.141:10870/interfaces/api/amr/robotQuery"; |
| | | |
| | | // // æå»ºå 餿¡ä»¶ï¼3个æåçæ¥å¿ æ (3天åä¸URL为ç¹å®å°åçæ¥å¿) |
| | | // var result = BaseDal.Db.Deleteable<Sys_Log>() |
| | | // .Where(log => log.EndDate < threeMonthsAgo || |
| | | // (log.EndDate < sevenDaysAgo && log.Url == specificUrl)) |
| | | // .ExecuteCommand(); |
| | | // // æ´æ°æåæ§è¡æ¥æä¸ºä»å¤© |
| | | // _lastExecutionDate = today; |
| | | // } |
| | | // catch (Exception ex) |
| | | // { |
| | | // Console.WriteLine($"æ§è¡æ¥å¿æ¸
çæ¶åçé误ï¼{ex.Message}"); |
| | | // // åçé误æ¶ä¸æ´æ°æåæ§è¡æ¥æï¼ä»¥ä¾¿éè¯ |
| | | // } |
| | | // } |
| | | // } |
| | | // // 15天æ§è¡ä¸æ¬¡ææå¤©2ç¹ |
| | | // public void CheckAndManageFileSize() |
| | | // { |
| | | // try |
| | | // { |
| | | |
| | | // // --æ£æ¥æ°æ®åºæä»¶ç©ºé´ä½¿ç¨æ
åµ |
| | | // //SELECT |
| | | // // name AS[æä»¶é»è¾å], |
| | | // // physical_name AS[ç©çè·¯å¾], |
| | | // // size/ 128.0 AS[å½å大å°MB], |
| | | // // FILEPROPERTY(name, 'SpaceUsed') / 128.0 AS[å·²ç¨ç©ºé´MB], |
| | | // // size / 128.0 - FILEPROPERTY(name, 'SpaceUsed') / 128.0 AS[å¯ç¨ç©ºé´MB], |
| | | // // growth / 128 AS[å¢é¿éMB], |
| | | // // CASE is_percent_growth |
| | | // // WHEN 1 THEN CAST(growth AS VARCHAR) +'%' |
| | | // // ELSE CAST(growth/ 128 AS VARCHAR) +'MB' |
| | | // // END AS[å¢é¿æ¹å¼] |
| | | // //FROM sys.database_files; |
| | | // using (var connection = new SqlConnection("Data Source=.;Initial Catalog=WIDESEA_HF;User ID=sa;Password=123456;Integrated Security=False;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False")) |
| | | // { |
| | | // connection.Open(); |
| | | |
| | | // // æ£æ¥æ°æ®åºæä»¶å¤§å° |
| | | // var sizeCheckSql = @"SELECT name AS [æä»¶é»è¾å], size/128.0 AS [å½å大å°MB], FILEPROPERTY(name, 'SpaceUsed')/128.0 AS [å·²ç¨ç©ºé´MB] FROM sys.database_files WHERE name = @dbName"; |
| | | |
| | | // using (var command = new SqlCommand(sizeCheckSql, connection)) |
| | | // { |
| | | // command.Parameters.AddWithValue("@dbName", DbName); |
| | | // using (var reader = command.ExecuteReader()) |
| | | // { |
| | | // if (reader.Read()) |
| | | // { |
| | | // double currentSizeMB = Convert.ToDouble(reader["å½å大å°MB"]); |
| | | // double usedSpaceMB = Convert.ToDouble(reader["å·²ç¨ç©ºé´MB"]); |
| | | |
| | | // if (currentSizeMB > MaxMdfSizeBytes / (1024 * 1024)) |
| | | // { |
| | | // // æ§è¡æ¶ç¼©æ°æ®åº |
| | | |
| | | // }else |
| | | // { |
| | | // // æ´çç¢ç>30% |
| | | |
| | | // } |
| | | // } |
| | | // } |
| | | // } |
| | | // } |
| | | // } |
| | | // catch (Exception ex) |
| | | // { |
| | | // throw new Exception($"æä»¶çæ§å¤±è´¥: {ex.Message}"); |
| | | // } |
| | | // } |
| | | |
| | | |
| | | // } |
| | | //} |
| | | using Microsoft.Data.SqlClient; |
| | | using NPOI.HSSF.Record; |
| | | using SkiaSharp; |
| | | using System; |
| | | using System.Collections.Generic; |
| | | using System.Linq; |
| | |
| | | public class Sys_LogService : ServiceBase<Sys_Log, ISys_LogRepository>, ISys_LogService |
| | | { |
| | | private static DateTime? _lastExecutionDate = null; |
| | | private static DateTime? _lastFileSizeCheckDate = null; |
| | | private static DateTime? _lastBackupDate = null; |
| | | private readonly object _lock = new object(); |
| | | private readonly object _fileSizeLock = new object(); |
| | | private readonly object _backupLock = new object(); |
| | | |
| | | private const string DbName = "WIDESEA_HF"; |
| | | private const string ConnectionString = "Data Source=11.2.30.112;Initial Catalog=WIDESEA_HF;User ID=kuka;Password=kuka;Integrated Security=False;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False"; |
| | | private const long MaxMdfSizeBytes = 8L * 1024 * 1024 * 1024; // 8GB |
| | | private const int TargetShrinkSizeMB = 4500; // 4.5GB |
| | | private const double FragmentationThreshold = 30.0; // 30%ç¢ççéå¼ |
| | | private const int MaintenanceIntervalDays = 15; // 15天æ§è¡ä¸æ¬¡ |
| | | private const int BackupRetentionDays = 30; // å¤ä»½ä¿ç30天 |
| | | private const string BackupBasePath = @"D:\DatabaseBackups"; // å¤ä»½æä»¶åå¨ç®å½ |
| | | private const bool UseBackupCompression = true; // æ¯å¦ä½¿ç¨å¤ä»½å缩 |
| | | private const bool VerifyBackup = true; // æ¯å¦éªè¯å¤ä»½ |
| | | private const int MaxBackupRetryCount = 3; // æå¤§éè¯æ¬¡æ° |
| | | private const int BackupTimeoutSeconds = 7200; // å¤ä»½è¶
æ¶æ¶é´ï¼ç§ï¼ |
| | | |
| | | public Sys_LogService(ISys_LogRepository BaseDal) : base(BaseDal) |
| | | { |
| | | // ç¡®ä¿å¤ä»½ç®å½åå¨ |
| | | EnsureBackupDirectory(); |
| | | } |
| | | |
| | | private void EnsureBackupDirectory() |
| | | { |
| | | try |
| | | { |
| | | if (!Directory.Exists(BackupBasePath)) |
| | | { |
| | | Directory.CreateDirectory(BackupBasePath); |
| | | } |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | Console.WriteLine($"å建å¤ä»½ç®å½å¤±è´¥: {ex.Message}"); |
| | | throw; |
| | | } |
| | | } |
| | | |
| | | public void DeleteOldLogs() |
| | |
| | | // 计ç®3个æåçæ¥æ |
| | | var threeMonthsAgo = DateTime.Now.AddMonths(-3); |
| | | // 计ç®3天åçæ¥æ |
| | | var sevenDaysAgo = DateTime.Now.AddDays(-3); |
| | | var threeDaysAgo = DateTime.Now.AddDays(-3); |
| | | // ç¹å®URL |
| | | var specificUrl = "http://11.2.30.141:10870/interfaces/api/amr/robotQuery"; |
| | | var anonUrl = "http://11.2.30.111/anon/api/kuka/drivestatus"; |
| | | |
| | | // æå»ºå 餿¡ä»¶ï¼3个æåçæ¥å¿ æ (7天åä¸URL为ç¹å®å°åçæ¥å¿) |
| | | // æå»ºå 餿¡ä»¶ï¼3个æåçæ¥å¿ æ (3天åä¸URL为ç¹å®å°åçæ¥å¿) |
| | | var result = BaseDal.Db.Deleteable<Sys_Log>() |
| | | .Where(log => log.EndDate < threeMonthsAgo || |
| | | (log.EndDate < sevenDaysAgo && log.Url == specificUrl)) |
| | | (log.EndDate < threeDaysAgo && (log.Url == specificUrl || log.Url == anonUrl))) |
| | | .ExecuteCommand(); |
| | | // æ´æ°æåæ§è¡æ¥æä¸ºä»å¤© |
| | | _lastExecutionDate = today; |
| | |
| | | catch (Exception ex) |
| | | { |
| | | Console.WriteLine($"æ§è¡æ¥å¿æ¸
çæ¶åçé误ï¼{ex.Message}"); |
| | | throw new Exception($"æ¥å¿æ¸
çæ¶åçé误: {ex.Message}", ex); |
| | | // åçé误æ¶ä¸æ´æ°æåæ§è¡æ¥æï¼ä»¥ä¾¿éè¯ |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 15天æ§è¡ä¸æ¬¡ææå¤©2ç¹ |
| | | public void CheckAndManageFileSize() |
| | | { |
| | | // æ£æ¥æ¯å¦ç¬¦åæ§è¡æ¡ä»¶ï¼ææå¤©ã2ç¹å·¦å³ãè·ç¦»ä¸æ¬¡æ§è¡è³å°15天 |
| | | //if (!ShouldExecuteMaintenance()) |
| | | //{ |
| | | // return; |
| | | //} |
| | | |
| | | try |
| | | { |
| | | using (var connection = new SqlConnection(ConnectionString)) |
| | | { |
| | | connection.Open(); |
| | | |
| | | // 1. å¤ä»½æ°æ®åº |
| | | BackupDatabase(); |
| | | |
| | | // 2. æ£æ¥æ°æ®åºæä»¶å¤§å° |
| | | CheckDatabaseFileSize(connection); |
| | | |
| | | // 3. æ£æ¥å¹¶å¤çç´¢å¼ç¢ç |
| | | CheckAndHandleIndexFragmentation(connection); |
| | | |
| | | // 4. æ´æ°ç»è®¡ä¿¡æ¯ |
| | | UpdateStatistics(connection); |
| | | |
| | | // 5. æ¸
çæ§å¤ä»½ |
| | | CleanupOldBackups(); |
| | | |
| | | // æ´æ°æåæ£æ¥æ¥æ |
| | | lock (_fileSizeLock) |
| | | { |
| | | _lastFileSizeCheckDate = DateTime.Now; |
| | | } |
| | | } |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | Console.WriteLine($"æä»¶çæ§å¤±è´¥: {ex.Message}"); |
| | | throw new Exception($"æä»¶çæ§å¤±è´¥: {ex.Message}", ex); |
| | | } |
| | | } |
| | | |
| | | public void BackupDatabase() |
| | | { |
| | | lock (_backupLock) |
| | | { |
| | | try |
| | | { |
| | | var today = DateTime.Today; |
| | | // æ£æ¥ä»å¤©æ¯å¦å·²ç»å¤ä»½è¿ |
| | | if (_lastBackupDate.HasValue && _lastBackupDate.Value.Date == today) |
| | | { |
| | | return; |
| | | } |
| | | var timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss"); |
| | | var backupFileName = $"{DbName}_Full_{timestamp}.bak"; |
| | | var backupFilePath = Path.Combine(BackupBasePath, backupFileName); |
| | | |
| | | using (var connection = new SqlConnection(ConnectionString)) |
| | | { |
| | | connection.Open(); |
| | | // è·åæ°æ®åºæä»¶ä¿¡æ¯ï¼è®¡ç®å¤ä»½æä»¶å¤§å° |
| | | var dbSizeQuery = @" |
| | | SELECT |
| | | SUM(size/128.0) AS TotalSizeMB, |
| | | SUM(FILEPROPERTY(name, 'SpaceUsed')/128.0) AS UsedSizeMB, |
| | | COUNT(*) AS FileCount |
| | | FROM sys.database_files |
| | | WHERE type IN (0, 1)"; // æ°æ®æä»¶åæ¥å¿æä»¶ |
| | | |
| | | double totalSizeMB = 0; |
| | | double usedSizeMB = 0; |
| | | int fileCount = 0; |
| | | |
| | | using (var cmd = new SqlCommand(dbSizeQuery, connection)) |
| | | using (var reader = cmd.ExecuteReader()) |
| | | { |
| | | if (reader.Read()) |
| | | { |
| | | totalSizeMB = Convert.ToDouble(reader["TotalSizeMB"]); |
| | | usedSizeMB = Convert.ToDouble(reader["UsedSizeMB"]); |
| | | fileCount = Convert.ToInt32(reader["FileCount"]); |
| | | } |
| | | } |
| | | |
| | | |
| | | // æ£æ¥ç£çç©ºé´æ¯å¦è¶³å¤ |
| | | if (!CheckDiskSpace(backupFilePath, totalSizeMB)) |
| | | { |
| | | throw new Exception("ç£ç空é´ä¸è¶³ï¼æ æ³æ§è¡å¤ä»½"); |
| | | } |
| | | |
| | | // æå»ºå¤ä»½SQLè¯å¥ |
| | | var backupSql = BuildBackupSql(backupFilePath); |
| | | |
| | | // æ§è¡å¤ä»½ï¼å¸¦éè¯æºå¶ï¼ |
| | | ExecuteBackupWithRetry(connection, backupSql, backupFilePath); |
| | | |
| | | // éªè¯å¤ä»½æä»¶ |
| | | if (VerifyBackup) |
| | | { |
| | | VerifyBackupFile(connection, backupFilePath); |
| | | } |
| | | |
| | | // æ´æ°æåå¤ä»½æ¥æ |
| | | _lastBackupDate = DateTime.Now; |
| | | |
| | | } |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | Console.WriteLine($"æ°æ®åºå¤ä»½å¤±è´¥: {ex.Message}"); |
| | | throw new Exception($"æ°æ®åºå¤ä»½å¤±è´¥: {ex.Message}", ex); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private bool CheckDiskSpace(string backupFilePath, double estimatedBackupSizeMB) |
| | | { |
| | | try |
| | | { |
| | | var driveInfo = new DriveInfo(Path.GetPathRoot(backupFilePath)); |
| | | var freeSpaceMB = driveInfo.AvailableFreeSpace / (1024 * 1024); |
| | | |
| | | // éè¦è³å°1.5åçç©ºé´æ¥ç¡®ä¿å¤ä»½é¡ºå©è¿è¡ |
| | | var requiredSpaceMB = estimatedBackupSizeMB * 1.5; |
| | | |
| | | if (freeSpaceMB < requiredSpaceMB) |
| | | { |
| | | return false; |
| | | } |
| | | |
| | | return true; |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | Console.WriteLine($"æ£æ¥ç£çç©ºé´æ¶åºé: {ex.Message}"); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | private string BuildBackupSql(string backupFilePath) |
| | | { |
| | | var compressionOption = UseBackupCompression ? "COMPRESSION" : "NO_COMPRESSION"; |
| | | var verifyOption = VerifyBackup ? ", CHECKSUM" : ""; |
| | | |
| | | return $@" |
| | | BACKUP DATABASE [{DbName}] |
| | | TO DISK = '{backupFilePath.Replace("'", "''")}' |
| | | WITH {compressionOption} |
| | | {verifyOption} |
| | | , STATS = 5 |
| | | , DESCRIPTION = 'Full database backup created by automated maintenance on {DateTime.Now:yyyy-MM-dd HH:mm:ss}' |
| | | , MAXTRANSFERSIZE = 4194304 |
| | | , BLOCKSIZE = 65536 |
| | | , BUFFERCOUNT = 10"; |
| | | } |
| | | |
| | | private void ExecuteBackupWithRetry(SqlConnection connection, string backupSql, string backupFilePath) |
| | | { |
| | | int retryCount = 0; |
| | | while (retryCount < MaxBackupRetryCount) |
| | | { |
| | | try |
| | | { |
| | | |
| | | using (var cmd = new SqlCommand(backupSql, connection)) |
| | | { |
| | | cmd.CommandTimeout = BackupTimeoutSeconds; |
| | | |
| | | var rowsAffected = cmd.ExecuteNonQuery(); |
| | | return; // æååéåº |
| | | } |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | retryCount++; |
| | | |
| | | if (retryCount >= MaxBackupRetryCount) |
| | | { |
| | | // å é¤å¯è½æåçå¤ä»½æä»¶ |
| | | try |
| | | { |
| | | if (File.Exists(backupFilePath)) |
| | | { |
| | | File.Delete(backupFilePath); |
| | | } |
| | | } |
| | | catch { } |
| | | |
| | | throw new Exception($"å¤ä»½å¤±è´¥ï¼å·²è¾¾å°æå¤§éè¯æ¬¡æ°({MaxBackupRetryCount}次): {ex.Message}", ex); |
| | | } |
| | | |
| | | Console.WriteLine($"å¤ä»½å¤±è´¥ï¼{MaxBackupRetryCount - retryCount}次éè¯æºä¼: {ex.Message}"); |
| | | Thread.Sleep(5000); // çå¾
5ç§åéè¯ |
| | | } |
| | | } |
| | | } |
| | | |
| | | private void VerifyBackupFile(SqlConnection connection, string backupFilePath) |
| | | { |
| | | try |
| | | { |
| | | |
| | | var verifySql = $@" |
| | | RESTORE VERIFYONLY |
| | | FROM DISK = '{backupFilePath.Replace("'", "''")}' |
| | | WITH CHECKSUM"; |
| | | |
| | | using (var cmd = new SqlCommand(verifySql, connection)) |
| | | { |
| | | cmd.CommandTimeout = 300; // 5åéè¶
æ¶ |
| | | cmd.ExecuteNonQuery(); |
| | | } |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | // éªè¯å¤±è´¥æ¶å é¤å¤ä»½æä»¶ |
| | | try |
| | | { |
| | | if (File.Exists(backupFilePath)) |
| | | { |
| | | File.Delete(backupFilePath); |
| | | } |
| | | } |
| | | catch { } |
| | | |
| | | throw new Exception($"å¤ä»½æä»¶éªè¯å¤±è´¥: {ex.Message}", ex); |
| | | } |
| | | } |
| | | |
| | | private void CleanupOldBackups() |
| | | { |
| | | try |
| | | { |
| | | |
| | | if (!Directory.Exists(BackupBasePath)) |
| | | { |
| | | Console.WriteLine("å¤ä»½ç®å½ä¸åå¨ï¼è·³è¿æ¸
ç"); |
| | | return; |
| | | } |
| | | |
| | | var cutoffDate = DateTime.Now.AddDays(-BackupRetentionDays); |
| | | var backupFiles = Directory.GetFiles(BackupBasePath, $"{DbName}_Full_*.bak"); |
| | | int deletedCount = 0; |
| | | |
| | | foreach (var file in backupFiles) |
| | | { |
| | | try |
| | | { |
| | | var fileInfo = new FileInfo(file); |
| | | if (fileInfo.CreationTime < cutoffDate) |
| | | { |
| | | fileInfo.Delete(); |
| | | deletedCount++; |
| | | } |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | Console.WriteLine($"å é¤å¤ä»½æä»¶ {file} æ¶åºé: {ex.Message}"); |
| | | // ç»§ç»å¤çå
¶ä»æä»¶ |
| | | } |
| | | } |
| | | |
| | | |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | Console.WriteLine($"æ¸
çæ§å¤ä»½æ¶åºé: {ex.Message}"); |
| | | // 䏿åºå¼å¸¸ï¼é¿å
å½±å主æµç¨ |
| | | } |
| | | } |
| | | |
| | | private bool ShouldExecuteMaintenance() |
| | | { |
| | | lock (_fileSizeLock) |
| | | { |
| | | var now = DateTime.Now; |
| | | |
| | | // æ£æ¥ä»å¤©æ¯å¦æ¯ææå¤© |
| | | if (now.DayOfWeek != DayOfWeek.Sunday) |
| | | { |
| | | return false; |
| | | } |
| | | |
| | | // æ£æ¥æ¶é´æ¯å¦æ¯1ç¹å·¦å³ï¼å
许1:00-1:59ï¼ |
| | | if (now.Hour != 1) |
| | | { |
| | | return false; |
| | | } |
| | | |
| | | // æ£æ¥æ¯å¦å·²ç»æ§è¡è¿ |
| | | if (_lastFileSizeCheckDate.HasValue) |
| | | { |
| | | // æ£æ¥è·ç¦»ä¸æ¬¡æ§è¡æ¯å¦æ»¡è¶³15天é´é |
| | | var daysSinceLastCheck = (now - _lastFileSizeCheckDate.Value).TotalDays; |
| | | if (daysSinceLastCheck < MaintenanceIntervalDays) |
| | | { |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | return true; |
| | | } |
| | | } |
| | | |
| | | private void CheckDatabaseFileSize(SqlConnection connection) |
| | | { |
| | | try |
| | | { |
| | | var sizeCheckSql = @" |
| | | SELECT |
| | | name AS [æä»¶é»è¾å], |
| | | physical_name AS [ç©çè·¯å¾], |
| | | size/128.0 AS [å½å大å°MB], |
| | | FILEPROPERTY(name, 'SpaceUsed')/128.0 AS [å·²ç¨ç©ºé´MB], |
| | | size/128.0 - FILEPROPERTY(name, 'SpaceUsed')/128.0 AS [å¯ç¨ç©ºé´MB], |
| | | max_size/128.0 AS [æå¤§å¤§å°MB], |
| | | growth/128.0 AS [å¢é¿éMB], |
| | | CASE is_percent_growth |
| | | WHEN 1 THEN 'ç¾åæ¯å¢é¿' |
| | | ELSE 'åºå®å¤§å°å¢é¿' |
| | | END AS [å¢é¿æ¹å¼] |
| | | FROM sys.database_files |
| | | WHERE type = 0"; // 0è¡¨ç¤ºæ°æ®æä»¶ï¼1表示æ¥å¿æä»¶ |
| | | |
| | | using (var command = new SqlCommand(sizeCheckSql, connection)) |
| | | { |
| | | using (var reader = command.ExecuteReader()) |
| | | { |
| | | while (reader.Read()) |
| | | { |
| | | string logicalName = reader["æä»¶é»è¾å"].ToString(); |
| | | double currentSizeMB = Convert.ToDouble(reader["å½å大å°MB"]); |
| | | double usedSpaceMB = Convert.ToDouble(reader["å·²ç¨ç©ºé´MB"]); |
| | | double freeSpaceMB = Convert.ToDouble(reader["å¯ç¨ç©ºé´MB"]); |
| | | |
| | | |
| | | // æ£æ¥æ¯å¦éè¦æ¶ç¼© |
| | | if (currentSizeMB * 1024 * 1024 > MaxMdfSizeBytes) |
| | | { |
| | | Console.WriteLine("æ°æ®åºæä»¶è¶
è¿8GBï¼å¼å§æ¶ç¼©..."); |
| | | ShrinkDatabaseFile(connection, logicalName); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | Console.WriteLine($"æ£æ¥æ°æ®åºæä»¶å¤§å°æ¶åºé: {ex.Message}"); |
| | | throw; |
| | | } |
| | | } |
| | | |
| | | private void ShrinkDatabaseFile(SqlConnection connection, string logicalName) |
| | | { |
| | | try |
| | | { |
| | | |
| | | // æ¶ç¼©æ°æ®åºæä»¶ |
| | | string shrinkSql = $"DBCC SHRINKFILE (N'{logicalName}', {TargetShrinkSizeMB})"; |
| | | using (var command = new SqlCommand(shrinkSql, connection)) |
| | | { |
| | | int rowsAffected = command.ExecuteNonQuery(); |
| | | Console.WriteLine($"æ°æ®åºæä»¶æ¶ç¼©å®æï¼ç®æ 大å°: {TargetShrinkSizeMB} MB"); |
| | | } |
| | | |
| | | // éæ°ç»ç»ç´¢å¼ä»¥é¿å
æ¶ç¼©å¯¼è´çç¢ç |
| | | ReorganizeAllIndexes(connection); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | Console.WriteLine($"æ¶ç¼©æ°æ®åºæä»¶æ¶åºé: {ex.Message}"); |
| | | throw; |
| | | } |
| | | } |
| | | |
| | | private void CheckAndHandleIndexFragmentation(SqlConnection connection) |
| | | { |
| | | try |
| | | { |
| | | |
| | | // æ¥è¯¢ææè¡¨çç´¢å¼ç¢çç |
| | | string fragmentationSql = @" |
| | | SELECT |
| | | OBJECT_NAME(ips.object_id) AS TableName, |
| | | i.name AS IndexName, |
| | | ips.index_type_desc AS IndexType, |
| | | ips.avg_fragmentation_in_percent AS FragmentationPercent, |
| | | ips.page_count AS PageCount |
| | | FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, 'LIMITED') ips |
| | | INNER JOIN sys.indexes i ON ips.object_id = i.object_id AND ips.index_id = i.index_id |
| | | WHERE ips.avg_fragmentation_in_percent > 0 |
| | | AND ips.index_id > 0 -- æé¤å |
| | | AND ips.page_count > 100 -- åªå¤çè¶
è¿100页çç´¢å¼ |
| | | ORDER BY ips.avg_fragmentation_in_percent DESC"; |
| | | |
| | | using (var command = new SqlCommand(fragmentationSql, connection)) |
| | | using (var reader = command.ExecuteReader()) |
| | | { |
| | | var indexesToReorganize = new List<(string TableName, string IndexName, double Fragmentation)>(); |
| | | |
| | | while (reader.Read()) |
| | | { |
| | | string tableName = reader["TableName"].ToString(); |
| | | string indexName = reader["IndexName"].ToString(); |
| | | double fragmentation = Convert.ToDouble(reader["FragmentationPercent"]); |
| | | long pageCount = Convert.ToInt64(reader["PageCount"]); |
| | | |
| | | Console.WriteLine($"表: {tableName}, ç´¢å¼: {indexName}, ç¢çç: {fragmentation:F2}%, 页æ°: {pageCount}"); |
| | | |
| | | if (fragmentation > FragmentationThreshold) |
| | | { |
| | | indexesToReorganize.Add((tableName, indexName, fragmentation)); |
| | | } |
| | | } |
| | | |
| | | reader.Close(); // å
³éreader以æ§è¡åç»å½ä»¤ |
| | | |
| | | // å¤çéè¦éæ°ç»ç»çç´¢å¼ |
| | | if (indexesToReorganize.Any()) |
| | | { |
| | | |
| | | foreach (var index in indexesToReorganize) |
| | | { |
| | | ReorganizeIndex(connection, index.TableName, index.IndexName); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | Console.WriteLine($"æ£æ¥ç´¢å¼ç¢çæ¶åºé: {ex.Message}"); |
| | | throw; |
| | | } |
| | | } |
| | | |
| | | private void ReorganizeIndex(SqlConnection connection, string tableName, string indexName) |
| | | { |
| | | try |
| | | { |
| | | string reorganizeSql = $"ALTER INDEX [{indexName}] ON [{tableName}] REORGANIZE"; |
| | | using (var command = new SqlCommand(reorganizeSql, connection)) |
| | | { |
| | | command.CommandTimeout = 600; // 设置10åéè¶
æ¶ |
| | | command.ExecuteNonQuery(); |
| | | } |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | Console.WriteLine($"éæ°ç»ç»ç´¢å¼ {tableName}.{indexName} æ¶åºé: {ex.Message}"); |
| | | // ç»§ç»å¤çå
¶ä»ç´¢å¼ |
| | | } |
| | | } |
| | | |
| | | private void ReorganizeAllIndexes(SqlConnection connection) |
| | | { |
| | | try |
| | | { |
| | | string reorganizeAllSql = @" |
| | | DECLARE @TableName NVARCHAR(128) |
| | | DECLARE @IndexName NVARCHAR(128) |
| | | DECLARE @Sql NVARCHAR(MAX) |
| | | |
| | | DECLARE IndexCursor CURSOR FOR |
| | | SELECT |
| | | OBJECT_NAME(i.object_id) AS TableName, |
| | | i.name AS IndexName |
| | | FROM sys.indexes i |
| | | WHERE i.index_id > 0 -- æé¤å |
| | | AND OBJECT_NAME(i.object_id) IS NOT NULL |
| | | |
| | | OPEN IndexCursor |
| | | FETCH NEXT FROM IndexCursor INTO @TableName, @IndexName |
| | | |
| | | WHILE @@FETCH_STATUS = 0 |
| | | BEGIN |
| | | SET @Sql = 'ALTER INDEX [' + @IndexName + '] ON [' + @TableName + '] REORGANIZE' |
| | | BEGIN TRY |
| | | EXEC sp_executesql @Sql |
| | | PRINT '已鿰ç»ç»ç´¢å¼: ' + @TableName + '.' + @IndexName |
| | | END TRY |
| | | BEGIN CATCH |
| | | PRINT 'éæ°ç»ç»ç´¢å¼ ' + @TableName + '.' + @IndexName + ' æ¶åºé: ' + ERROR_MESSAGE() |
| | | END CATCH |
| | | |
| | | FETCH NEXT FROM IndexCursor INTO @TableName, @IndexName |
| | | END |
| | | |
| | | CLOSE IndexCursor |
| | | DEALLOCATE IndexCursor"; |
| | | |
| | | using (var command = new SqlCommand(reorganizeAllSql, connection)) |
| | | { |
| | | command.CommandTimeout = 1800; // 设置30åéè¶
æ¶ |
| | | command.ExecuteNonQuery(); |
| | | } |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | Console.WriteLine($"éæ°ç»ç»ææç´¢å¼æ¶åºé: {ex.Message}"); |
| | | throw; |
| | | } |
| | | } |
| | | private void UpdateStatistics(SqlConnection connection) |
| | | { |
| | | try |
| | | { |
| | | // æ´æ°ææè¡¨çç»è®¡ä¿¡æ¯ |
| | | string updateStatsSql = "EXEC sp_updatestats"; |
| | | using (var command = new SqlCommand(updateStatsSql, connection)) |
| | | { |
| | | command.CommandTimeout = 600; // 设置10åéè¶
æ¶ |
| | | command.ExecuteNonQuery(); |
| | | } |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | Console.WriteLine($"æ´æ°ç»è®¡ä¿¡æ¯æ¶åºé: {ex.Message}"); |
| | | throw; |
| | | } |
| | | } |
| | | // æä¾ä¸ä¸ªæ¹æ³ä¾ä»»å¡è°åº¦å¨è°ç¨ |
| | | public void ScheduleDatabaseMaintenance() |
| | | { |
| | | try |
| | | { |
| | | // 1. å 餿§æ¥å¿ |
| | | DeleteOldLogs(); |
| | | // æ£æ¥æ¯å¦ç¬¦åæ§è¡æ¡ä»¶ |
| | | if (!ShouldExecuteMaintenance()) |
| | | { |
| | | return; |
| | | } |
| | | // 2. æ£æ¥å管çæä»¶å¤§å° |
| | | CheckAndManageFileSize(); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | Console.WriteLine($"æ§è¡æ°æ®åºç»´æ¤ä»»å¡æ¶åºé: {ex.Message}"); |
| | | throw; |
| | | } |
| | | } |
| | | } |
| | | } |