Database
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Database

Sách thứ hai có thể đọc được trên ngân sách

Nhóm khả dụng, được giới thiệu trong SQL Server 2012, đại diện cho một sự thay đổi cơ bản trong cách chúng tôi nghĩ về cả tính khả dụng cao và khả năng phục hồi sau thảm họa cho cơ sở dữ liệu của chúng tôi. Một trong những điều tuyệt vời có thể thực hiện được ở đây là giảm tải các hoạt động chỉ đọc cho một bản sao thứ cấp, để cá thể đọc / ghi chính không bị làm phiền bởi những thứ phiền phức như báo cáo của người dùng cuối. Việc thiết lập điều này không đơn giản, nhưng dễ dàng hơn và dễ bảo trì hơn rất nhiều so với các giải pháp trước đây (giơ tay lên nếu bạn thích thiết lập phản chiếu và ảnh chụp nhanh cũng như tất cả các bảo trì vĩnh viễn liên quan đến điều đó).

Mọi người rất hào hứng khi họ nghe về Nhóm sẵn sàng. Sau đó, thực tế xảy ra:tính năng yêu cầu Phiên bản Doanh nghiệp của SQL Server (dù sao cũng như SQL Server 2014). Phiên bản Doanh nghiệp đắt tiền, đặc biệt nếu bạn có nhiều lõi và đặc biệt là kể từ khi loại bỏ cấp phép dựa trên CAL (trừ khi bạn là người lớn từ 2008 R2, trong trường hợp đó bạn bị giới hạn ở 20 lõi đầu tiên). Nó cũng yêu cầu Windows Server Failover Clustering (WSFC), một sự phức tạp không chỉ để trình diễn công nghệ trên máy tính xách tay mà còn yêu cầu Enterprise Edition của Windows, bộ điều khiển miền và toàn bộ cấu hình để hỗ trợ phân cụm. Và cũng có những yêu cầu mới xung quanh Bảo hiểm phần mềm; một chi phí bổ sung nếu bạn muốn các phiên bản chờ của mình tuân thủ.

Một số khách hàng không thể biện minh cho giá cả. Những người khác nhìn thấy giá trị, nhưng chỉ đơn giản là không thể mua được. Vậy những người dùng này phải làm gì?

Anh hùng mới của bạn:Đăng nhập vận chuyển

Vận chuyển nhật ký đã có từ lâu. Nó đơn giản và nó chỉ hoạt động. Gần như luôn luôn. Và ngoài việc bỏ qua chi phí cấp phép và các rào cản cấu hình do Nhóm sẵn sàng trình bày, nó cũng có thể tránh được hình phạt 14 byte mà Paul Randal (@PaulRandal) đã nói trong bản tin SQLskills Insider tuần này (ngày 13 tháng 10 năm 2014).

Tuy nhiên, một trong những thách thức mà mọi người gặp phải khi sử dụng bản sao đã gửi nhật ký làm bản sao thứ cấp có thể đọc được, đó là bạn phải loại bỏ tất cả những người dùng hiện tại để áp dụng bất kỳ nhật ký mới nào - vì vậy hoặc bạn sẽ khiến người dùng khó chịu vì họ liên tục bị gián đoạn từ các truy vấn đang chạy hoặc bạn khiến người dùng khó chịu vì dữ liệu của họ đã cũ. Điều này là do mọi người tự giới hạn mình ở một thứ cấp có thể đọc được.

Nó không nhất thiết phải như vậy; Tôi nghĩ rằng có một giải pháp hữu ích ở đây và mặc dù nó có thể đòi hỏi nhiều công việc ở phía trước hơn là bật Nhóm khả dụng, nhưng chắc chắn đây sẽ là một lựa chọn hấp dẫn đối với một số người.

Về cơ bản, chúng tôi có thể thiết lập một số thứ hai, nơi chúng tôi sẽ ghi nhật ký tàu và chỉ biến một trong số chúng trở thành thứ hai "hoạt động", bằng cách sử dụng cách tiếp cận vòng tròn. Công việc vận chuyển nhật ký biết nhật ký nào hiện đang hoạt động, vì vậy nó chỉ khôi phục nhật ký mới cho máy chủ "tiếp theo" bằng cách sử dụng WITH STANDBY lựa chọn. Ứng dụng báo cáo sử dụng cùng một thông tin để xác định chuỗi kết nối sẽ là gì cho báo cáo tiếp theo mà người dùng chạy trong thời gian chạy. Khi bản sao lưu nhật ký tiếp theo đã sẵn sàng, mọi thứ sẽ thay đổi từng cái một và phiên bản bây giờ sẽ trở thành tệp thứ cấp có thể đọc được mới được khôi phục bằng cách sử dụng WITH STANDBY .

Để giữ cho mô hình không phức tạp, giả sử chúng tôi có bốn phiên bản đóng vai trò là bản thứ hai có thể đọc được và chúng tôi thực hiện sao lưu nhật ký sau mỗi 15 phút. Tại bất kỳ thời điểm nào, chúng tôi sẽ có một thứ hai đang hoạt động ở chế độ chờ, với dữ liệu cũ hơn 15 phút và ba thứ hai ở chế độ chờ không phục vụ các truy vấn mới (nhưng vẫn có thể trả về kết quả cho các truy vấn cũ hơn).

Điều này sẽ hoạt động tốt nhất nếu không có truy vấn nào kéo dài hơn 45 phút. (Bạn có thể cần điều chỉnh các chu kỳ này tùy thuộc vào bản chất của các hoạt động chỉ đọc của bạn, số lượng người dùng đồng thời đang chạy các truy vấn dài hơn và liệu có bao giờ có thể làm gián đoạn người dùng bằng cách loại bỏ tất cả mọi người hay không.)

Nó cũng sẽ hoạt động tốt nhất nếu các truy vấn liên tiếp được chạy bởi cùng một người dùng có thể thay đổi chuỗi kết nối của họ (đây là logic cần có trong ứng dụng, mặc dù bạn có thể sử dụng từ đồng nghĩa hoặc dạng xem tùy thuộc vào kiến ​​trúc) và chứa dữ liệu khác nhau có đã thay đổi trong thời gian chờ đợi (giống như khi họ đang truy vấn cơ sở dữ liệu trực tiếp, thay đổi liên tục).

Với tất cả các giả định này, đây là một chuỗi sự kiện minh họa cho 75 phút đầu tiên khi chúng tôi triển khai:

sự kiện hình ảnh 12:00 (t0)
  • Nhật ký sao lưu t0
  • Loại bỏ người dùng khỏi trường hợp A
  • Khôi phục nhật ký t0 về phiên bản A (STANDBY)
  • Các truy vấn chỉ đọc mới sẽ chuyển đến phiên bản A
12:15 (t1)
  • Nhật ký sao lưu t1
  • Loại bỏ người dùng khỏi trường hợp B
  • Khôi phục nhật ký t0 về phiên bản B (NORECOVERY)
  • Khôi phục nhật ký t1 về phiên bản B (STANDBY)
  • Các truy vấn chỉ đọc mới sẽ chuyển đến phiên bản B
  • Các truy vấn chỉ đọc hiện tại đối với phiên bản A có thể tiếp tục chạy nhưng chậm hơn ~ 15 phút
  • 12:30 (t2)
    • Nhật ký sao lưu t2
    • Loại người dùng khỏi phiên bản C
    • Khôi phục nhật ký t0 -> t1 về phiên bản C (NORECOVERY)
    • Khôi phục nhật ký t2 về phiên bản C (STANDBY)
    • Các truy vấn chỉ đọc mới sẽ chuyển đến phiên bản C
    • Các truy vấn chỉ đọc hiện tại đối với các phiên bản A &B có thể tiếp tục chạy (chậm hơn 15-30 phút)
    12:45 (t3)
    • Nhật ký sao lưu t3
    • Loại bỏ người dùng khỏi trường hợp D
    • Khôi phục nhật ký t0 -> t2 về phiên bản D (NORECOVERY)
    • Khôi phục nhật ký t3 về phiên bản D (STANDBY)
    • Các truy vấn chỉ đọc mới sẽ chuyển đến phiên bản D
    • Các truy vấn chỉ đọc hiện tại đối với các phiên bản A, B &C có thể tiếp tục chạy (chậm hơn 15-45 phút)
    • 13:00 (t4)
      • Nhật ký sao lưu t4
      • Loại bỏ người dùng khỏi trường hợp A
      • Khôi phục nhật ký t1 -> t3 về phiên bản A (NORECOVERY)
      • Khôi phục nhật ký t4 về phiên bản A (STANDBY)
      • Các truy vấn chỉ đọc mới sẽ chuyển đến phiên bản A
      • Các truy vấn chỉ đọc hiện tại đối với các phiên bản B, C &D có thể tiếp tục chạy (chậm hơn 15-45 phút)
      • Các truy vấn vẫn chạy trên phiên bản A kể từ t0 -> ~ t1 (45-60 phút) sẽ bị hủy

      Điều đó có vẻ đơn giản đủ
      ; viết mã để xử lý tất cả những gì khó khăn hơn một chút. Sơ lược:

      1. Trên máy chủ chính (tôi sẽ gọi nó là BOSS ), tạo cơ sở dữ liệu. Trước khi nghĩ đến việc đi xa hơn nữa, hãy bật Cờ theo dõi 3226 để ngăn các thông báo sao lưu thành công phân tán nhật ký lỗi của SQL Server.
      2. Trên BOSS , thêm một máy chủ được liên kết cho mỗi máy chủ phụ (tôi sẽ gọi chúng là PEON1 -> PEON4 ).
      3. Ở một nơi nào đó có thể truy cập vào tất cả các máy chủ, tạo chia sẻ tệp để lưu trữ các bản sao lưu cơ sở dữ liệu / nhật ký và đảm bảo các tài khoản dịch vụ cho mỗi phiên bản có quyền truy cập đọc / ghi. Ngoài ra, mỗi phiên bản phụ cần phải có một vị trí được chỉ định cho tệp chờ.
      4. Trong một cơ sở dữ liệu tiện ích riêng biệt (hoặc MSDB, nếu bạn thích), hãy tạo các bảng chứa thông tin cấu hình về (các) cơ sở dữ liệu, tất cả các thư thứ hai và ghi nhật ký lịch sử sao lưu và khôi phục.
      5. Tạo các thủ tục được lưu trữ sẽ sao lưu cơ sở dữ liệu và khôi phục về các thư thứ hai WITH NORECOVERY , và sau đó áp dụng một nhật ký WITH STANDBY , và đánh dấu một phiên bản làm phiên bản chờ hiện tại. Các quy trình này cũng có thể được sử dụng để khởi tạo lại toàn bộ thiết lập vận chuyển nhật ký trong trường hợp có bất kỳ sự cố nào xảy ra.
      6. Tạo một công việc sẽ chạy 15 phút một lần, để thực hiện các tác vụ được mô tả ở trên:
        • sao lưu nhật ký
        • xác định phương thức thứ yếu để áp dụng bất kỳ bản sao lưu nhật ký chưa được áp dụng nào cho
        • khôi phục các nhật ký đó bằng các cài đặt thích hợp
      7. Tạo một quy trình đã lưu trữ (và / hoặc một chế độ xem?) sẽ cho (các) ứng dụng gọi điện biết chúng nên sử dụng thứ cấp nào cho bất kỳ truy vấn chỉ đọc mới nào.
      8. Tạo quy trình dọn dẹp để xóa lịch sử sao lưu nhật ký cho các nhật ký đã được áp dụng cho tất cả các nhật ký thứ hai (và có thể cũng để di chuyển hoặc xóa chính các tệp đó).
      9. Tăng cường giải pháp với khả năng xử lý lỗi và thông báo mạnh mẽ.

      Bước 1 - tạo cơ sở dữ liệu

      Phiên bản chính của tôi là Standard Edition, có tên là . \ BOSS . Trên trường hợp đó, tôi tạo một cơ sở dữ liệu đơn giản với một bảng:

       use [master]; GOCREATE DATABASE UserData; GOALTER DATABASE UserData SET RECOVERY FULL; GOUSE UserData; GOCREATE TABLE dbo.LastUpdate (EventTime DATETIME2); INSERT dbo.LastUpdate (EventTime) CHỌN SYSDATETIME (); 
       Sau đó, tôi tạo một công việc SQL Server Agent chỉ cập nhật dấu thời gian đó mỗi phút:

       CẬP NHẬT UserData.dbo.LastUpdate SET EventTime =SYSDATETIME (); 

      Điều đó chỉ tạo ra cơ sở dữ liệu ban đầu và mô phỏng hoạt động, cho phép chúng tôi xác thực cách tác vụ vận chuyển nhật ký xoay vòng qua từng thứ hai có thể đọc được. Tôi muốn tuyên bố rõ ràng rằng điểm của bài tập này không phải để kiểm tra căng thẳng việc vận chuyển nhật ký hoặc để chứng minh khối lượng mà chúng ta có thể xuyên thủng; đó hoàn toàn là một bài tập khác.

      Bước 2 - thêm máy chủ được liên kết

      Tôi có bốn phiên bản Express Edition phụ có tên là . \ PEON1 , . \ PEON2 , . \ PEON3 . \ PEON4 . Vì vậy, tôi đã chạy mã này bốn lần, thay đổi @s mỗi lần:

       USE [master]; GODECLARE @s NVARCHAR (128) =N '. \ PEON1', - lặp lại cho. \ PEON2,. \ PEON3,. \ PEON4 @t NVARCHAR (128) =N'true '; EXEC [master] .dbo.sp_addlinkedserver @server =@s, @srvproduct =N'SQL Server '; EXEC [master] .dbo.sp_addlinkedsrvlogin @rmtsrvname =@s, @useself =@t; EXEC [master] .dbo. sp_serveroption @server =@s, @optname =N'collation tương thích ', @optvalue =@t; EXEC [master] .dbo.sp_serveroption @server =@s, @optname =N'data access', @optvalue =@t; EXEC [master] .dbo.sp_serveroption @server =@s, @optname =N'rpc ', @optvalue =@t; EXEC [master] .dbo.sp_serveroption @server =@s, @optname =N'rpc out ', @optvalue =@t; 

      Bước 3 - xác thực (các) chia sẻ tệp

      Trong trường hợp của tôi, tất cả 5 phiên bản đều trên cùng một máy chủ, vì vậy tôi chỉ tạo một thư mục cho mỗi phiên bản: C:\ temp \ Peon1 \ , C:\ temp \ Peon2 \ , và như thế. Hãy nhớ rằng nếu thư thứ hai của bạn ở trên các máy chủ khác nhau, thì vị trí phải liên quan đến máy chủ đó, nhưng vẫn có thể truy cập được từ máy chủ chính (vì vậy, thông thường sẽ sử dụng đường dẫn UNC). Bạn nên xác thực rằng mỗi cá thể có thể ghi vào chia sẻ đó và bạn cũng nên xác thực rằng mỗi cá thể có thể ghi vào vị trí được chỉ định cho tệp chờ (tôi đã sử dụng cùng các thư mục cho chế độ chờ). Bạn có thể xác thực điều này bằng cách sao lưu một cơ sở dữ liệu nhỏ từ mỗi phiên bản vào từng vị trí được chỉ định của nó - đừng tiếp tục cho đến khi điều này hoạt động.

      Bước 4 - tạo bảng

      Tôi quyết định đặt dữ liệu này trong msdb , nhưng tôi không thực sự có cảm tình mạnh mẽ với hoặc chống lại việc tạo một cơ sở dữ liệu riêng biệt. Bảng đầu tiên tôi cần là bảng chứa thông tin về (các) cơ sở dữ liệu mà tôi sẽ ghi nhật ký vận chuyển:

       TẠO BẢNG dbo.PMAG_Databases (DatabaseName SYSNAME, LogBackupFrequency_Minutes SMALLINT NOT NULL DEFAULT (15), CONSTRAINT PK_DBS KEY CHÍNH (Tên cơ sở dữ liệu)); ĐI CHÈN dbo.PMAG_Databases (Tên cơ sở dữ liệu) CHỌN N'UserData '; 
        (Nếu bạn tò mò về cách đặt tên, PMAG là viết tắt của "Nhóm sẵn sàng cho người nghèo".)  

      Một bảng khác được yêu cầu là một bảng để chứa thông tin về các thư mục thứ hai, bao gồm các thư mục riêng lẻ và trạng thái hiện tại của chúng trong trình tự vận chuyển nhật ký.

       CREATE TABLE dbo.PMAG_Secondaries (DatabaseName SYSNAME, ServerInstance SYSNAME, CommonFolder VARCHAR (512) NOT NULL, DataFolder VARCHAR (512) NOT NULL, LogFolder VARCHAR (512) NOT NULL, StandByLocation VARCHAR (512) NOT NULL, IsCurrentStandStandStand NULL DEFAULT 0, CONSTRAINT PK_Sec KEY CHÍNH (DatabaseName, ServerInstance), CONSTRAINT FK_Sec_DBs FOREIGN KEY (DatabaseName) TÀI LIỆU THAM KHẢO dbo.PMAG_Databases (DatabaseName)); 

      Nếu bạn muốn sao lưu cục bộ từ máy chủ nguồn và yêu cầu các bản thứ hai khôi phục từ xa hoặc ngược lại, bạn có thể tách CommonFolder thành hai cột ( BackupFolder RestoreFolder ) và thực hiện các thay đổi có liên quan trong mã (sẽ không có nhiều thay đổi như vậy).

      Vì tôi có thể điền bảng này ít nhất một phần dựa trên thông tin trong sys.servers - lợi dụng thực tế là dữ liệu / nhật ký và các thư mục khác được đặt tên theo tên phiên bản:

       INSERT dbo.PMAG_Secondaries (DatabaseName, ServerInstance, CommonFolder, DataFolder, LogFolder, StandByLocation) SELECT DatabaseName =N'UserData ', ServerInstance =name, CommonFolder =' C:\ temp \ Peon '+ RIGHT (name, 1) + '\', DataFolder ='C:\ Program Files \ Microsoft SQL Server \ MSSQL12.PEON' + RIGHT (tên, 1) + '\ MSSQL \ DATA \', LogFolder ='C:\ Program Files \ Microsoft SQL Server \ MSSQL12.PEON '+ RIGHT (name, 1) +' \ MSSQL \ DATA \ ', StandByLocation =' C:\ temp \ Peon '+ RIGHT (name, 1) +' \ 'FROM sys.servers WHERE tên NHƯ N' . \ PEON [1-4] '; 

      Tôi cũng cần một bảng để theo dõi các bản sao lưu nhật ký riêng lẻ (không chỉ là bảng cuối cùng), vì trong nhiều trường hợp, tôi sẽ cần khôi phục nhiều tệp nhật ký theo một trình tự. Tôi có thể lấy thông tin này từ msdb.dbo.backupset , nhưng việc lấy những thứ như vị trí sẽ phức tạp hơn nhiều - và tôi có thể không kiểm soát được các công việc khác có thể làm sạch lịch sử sao lưu.

       CREATE TABLE dbo.PMAG_LogBackupHistory (DatabaseName SYSNAME, ServerInstance SYSNAME, BackupSetID INT NOT NULL, Location VARCHAR (2000) NOT NULL, BackupTime DATETIME NOT NULL DEFAULT SYSDATETIME (), CONSTRAINT PKAINTBH PRIMARY KEY (DatabaseName) FK_LBH_DBs NGOẠI KHÓA (Tên cơ sở dữ liệu) TÀI LIỆU THAM KHẢO dbo.PMAG_Databases (Tên cơ sở dữ liệu), CONSTRAINT FK_LBH_Sec NGOẠI KHÓA (Tên cơ sở dữ liệu, Máy chủ) TÀI LIỆU THAM KHẢO dbo.PMAG_Secondaries (Tên cơ sở dữ liệu, Máy chủ)); 
       Bạn có thể nghĩ rằng thật lãng phí khi lưu trữ một hàng cho mỗi thứ cấp và lưu trữ vị trí của mọi bản sao lưu, nhưng điều này là để kiểm tra trong tương lai - để xử lý trường hợp bạn di chuyển CommonFolder cho bất kỳ thứ cấp nào. 

      Và cuối cùng là lịch sử khôi phục nhật ký, vì vậy, tại bất kỳ thời điểm nào, tôi có thể thấy nhật ký nào đã được khôi phục và ở đâu, và công việc khôi phục có thể đảm bảo chỉ khôi phục các nhật ký chưa được khôi phục:

       CREATE TABLE dbo.PMAG_LogRestoreHistory (DatabaseName SYSNAME, ServerInstance SYSNAME, BackupSetID INT, RestoreTime DATETIME, CONSTRAINT PK_LRH KEY CHÍNH (Tên cơ sở dữ liệu, ServerInstance, BackupSetID), CONSTRAINT FK_LRH_DFINT_Dame_blog, KEY CONSTRAINT FK_LRH_DBs FOREIGN TỪ KHÓA NGOẠI LỆ (DatabaseName, ServerInstance) TÀI LIỆU THAM KHẢO dbo.PMAG_Secondaries (DatabaseName, ServerInstance)); 

      Bước 5 - khởi tạo thư thứ hai

      Chúng tôi cần một thủ tục được lưu trữ sẽ tạo ra một tệp sao lưu (và phản chiếu nó đến bất kỳ vị trí nào theo yêu cầu của các trường hợp khác nhau) và chúng tôi cũng sẽ khôi phục một nhật ký cho mỗi tệp thứ cấp để đặt tất cả chúng ở chế độ chờ. Tại thời điểm này, tất cả chúng sẽ khả dụng cho các truy vấn chỉ đọc, nhưng chỉ một truy vấn sẽ là chế độ chờ "hiện tại" tại bất kỳ thời điểm nào. Đây là thủ tục được lưu trữ sẽ xử lý cả bản sao lưu toàn bộ và bản sao lưu nhật ký giao dịch; khi một bản sao lưu đầy đủ được yêu cầu và @init được đặt thành 1, nó sẽ tự động khởi tạo lại quá trình vận chuyển nhật ký.

       TẠO THỦ TỤC [dbo]. [PMAG_Backup] @dbname SYSNAME, @type CHAR (3) ='bak', - hoặc 'trn' @init BIT =0 - chỉ được sử dụng với 'bak'ASBEGIN BẬT TÀI KHOẢN BẬT; - tạo một mẫu tên tệp DECLARE @now DATETIME =SYSDATETIME (); DECLARE @fn NVARCHAR (256) =@dbname + N'_ '+ CONVERT (CHAR (8), @now, 112) + RIGHT (REPLICATE (' 0 ', 6) + CONVERT (VARCHAR (32), DATEDIFF (SECOND) , CHUYỂN ĐỔI (DATE, @now), @now)), 6) + N '.' + Kiểu @; - tạo một lệnh sao lưu với MIRROR TO cho mỗi CommonFolder DECLARE riêng biệt @sql NVARCHAR (MAX) =N'BACKUP '+ CASE @type WHEN' bak 'THEN N' DATABASE 'ELSE N' LOG 'END + QUOTENAME (@dbname) + '' + STUFF ((CHỌN ĐIỂM ĐOÁN (13) + CHAR (10) + N 'MIRROR TO DISK =' '+ s.CommonFolder + @fn +' '' 'TỪ dbo.PMAG_Secondaries AS s WHERE s.DatabaseName =@dbname FOR XML PATH (''), TYPE) .value (N '. [1]', N'nvarchar (max) '), 1,9, N' ') + N' VỚI TÊN =N '' '+ @dbname + CASE @type WHEN' bak 'THÌ N'_PMAGFull' ELSE N'_PMAGLog 'END +' ', INIT, FORMAT' + CASE WHEN LEFT (CHUYỂN ĐỔI (NVARCHAR (128), SERVERPROPERTY (N'Edition ") )), 3) IN (N'Dev ', N'Ent') THEN N ', COMPRESSION;' ELSE N ';' CHẤM DỨT; EXEC [master] .sys.sp_executesql @sql; IF @type ='bak' AND @init =1 - khởi tạo nhật ký vận chuyển BEGIN EXEC dbo.PMAG_InitializeSecondaries @dbname =@dbname, @fn =@fn; KẾT THÚC NẾU @type ='trn' BEGIN - ghi lại thực tế là chúng tôi đã sao lưu nhật ký INSERT dbo.PMAG_LogBackupHistory (DatabaseName, ServerInstance, BackupSetID, Location) SELECT DatabaseName =@dbname, ServerInstance =s.ServerInstance, BackupSetID =MAX (b .backup_set_id), Location =s.CommonFolder + @fn FROM msdb.dbo.backupset AS b CROSS JOIN dbo.PMAG_Secondaries AS s WHERE b.name =@dbname + N'_PMAGLog 'AND s.DatabaseName =@dbname GROUP BY s. ServerInstance, s.CommonFolder + @fn; - khi chúng tôi đã sao lưu nhật ký, - hãy khôi phục chúng trên EXEC dbo.PMAG_RestoreLogs @dbname =@dbname; KẾT THÚC 

      Điều này lần lượt gọi hai thủ tục mà bạn có thể gọi riêng (nhưng rất có thể sẽ không). Đầu tiên, thủ tục sẽ khởi tạo các thư thứ hai trong lần chạy đầu tiên:

       ALTER THỦ TỤC dbo.PMAG_InitializeSecondaries @dbname SYSNAME, @fn VARCHAR (512) BẬT ASBEGIN ĐẶT SỐ TÀI KHOẢN; - xóa lịch sử / cài đặt hiện có (vì đây có thể là một lần khởi động lại) XÓA dbo.PMAG_LogBackupHistory WHERE DatabaseName =@dbname; XÓA dbo.PMAG_LogRestoreHistory WHERE DatabaseName =@dbname; CẬP NHẬT dbo.PMAG_Secondaries SET IsCurrentStandby =0 WHERE DatabaseName =@dbname; DECLARE @sql NVARCHAR (MAX) =N '', @files NVARCHAR (MAX) =N ''; - cần biết tên tệp hợp lý - có thể nhiều hơn hai SET @sql =N'SELECT @files =(CHỌN N '', CHUYỂN N '' '' '' + tên + '' '' '' ĐẾN N '' '' $ '' + CASE [type] WHEN 0 THEN N''df '' WHEN 1 THEN N''lf '' END + '' $ '' '' '' FROM '+ QUOTENAME (@dbname) + '.sys.database_files WHERE [type] IN (0,1) FOR XML PATH, TYPE) .value (N' '. [1]' ', N''nvarchar (max)' ');'; EXEC master.sys.sp_executesql @sql, N '@ tệp NVARCHAR (MAX) OUTPUT', @files =@files OUTPUT; SET @sql =N ''; - khôi phục - cần đường dẫn vật lý của tệp dữ liệu / nhật ký cho WITH MOVE - điều này có thể không thành công, hiển nhiên, nếu những đường dẫn + tên đó đã tồn tại cho một db khác SELECT @sql + =N'EXEC '+ QUOTENAME (ServerInstance) + N' .master.sys.sp_executesql N''RESTORE DATABASE '+ QUOTENAME (@dbname) + N' FROM DISK =N '' '' '+ CommonFolder + @fn + N' '' '' '+ N' VỚI THAY THẾ, PHỤC HỒI '+ THAY THẾ (THAY THẾ (THAY THẾ (@files, N' $ df $ ', DataFolder + @dbname + N'.mdf'), N '$ lf $', LogFolder + @dbname + N'.ldf '), N '' '', N '' '' '') + N ';' ';' + CHAR (13) + CHAR (10) TỪ dbo.PMAG_Secondaries WHERE DatabaseName =@dbname; EXEC [master] .sys.sp_executesql @sql; - sao lưu nhật ký cho cơ sở dữ liệu này EXEC dbo.PMAG_Backup @dbname =@dbname, @type ='trn'; - khôi phục nhật ký EXEC dbo.PMAG_RestoreLogs @dbname =@dbname, @PrepareAll =1; HẾT 

      Và sau đó là quy trình sẽ khôi phục nhật ký:

       TẠO THỦ TỤC dbo.PMAG_RestoreLogs @dbname SYSNAME, @PrepareAll BIT =0ASBEGIN BẬT TÀI KHOẢN BẬT; DECLARE @StandbyInstance SYSNAME, @CurrentInstance SYSNAME, @BackupSetID INT, @Location VARCHAR (512), @StandByLocation VARCHAR (512), @sql NVARCHAR (MAX), @rn INT; - lấy phiên bản dự phòng "tiếp theo" SELECT @StandbyInstance =MIN (ServerInstance) FROM dbo.PMAG_Secondaries WHERE IsCurrentStandby =0 AND ServerInstance> (CHỌN ServerInstance FROM dbo.PMAG_Secondaries WHERE IsCurrentStandBy =1); NẾU @StandbyInstance LÀ NULL - nó là lần cuối cùng hoặc bắt đầu lại BEGIN SELECT @StandbyInstance =MIN (ServerInstance) FROM dbo.PMAG_Secondaries; KẾT THÚC - tải phiên bản đó lên và vào STANDBY - đối với mỗi lần đăng nhập logbackuphistory không có trong logrestorehistory:- khôi phục và chèn nó vào logrestorehistory - đánh dấu cái cuối cùng là STANDBY - nếu @prepareAll là true, hãy đánh dấu tất cả những cái khác là NORECOVERY - trong trường hợp này chỉ nên có một, nhưng chỉ trong trường hợp DECLARE c CURSOR LOCAL FAST_FORWARD FOR SELECT bh.BackupSetID, s.ServerInstance, bh.Location, s.StandbyLocation, rn =ROW_NUMBER () HẾT (PARTITION BY s. ServerInstance ORDER BY bh.BackupSetID DESC) TỪ dbo.PMAG_LogBackupHistory AS bh INNER JOIN dbo.PMAG_Secondaries AS s ON bh.DatabaseName =s.DatabaseName AND bh.ServerInstance =s.ServerInstance AND WHERE s.DatabaseName =@ CASEdbname AND s.DatabaseName =@ @PrepareAll WHEN 1 THEN s.ServerInstance ELSE @StandbyInstance KẾT THÚC VÀ KHÔNG TỒN TẠI (CHỌN 1 TỪ dbo.PMAG_LogRestoreHistory AS rh WHERE DatabaseName =@dbname AND ServerInstance =s.ServerInstance AND BackupSetID =bh.B ackupSetID) ĐẶT HÀNG THEO TRƯỜNG HỢP s.ServerInstance KHI @StandbyInstance THÌ 1 ELSE 2 END, bh.BackupSetID; MỞ c; NHẬP c VÀO @BackupSetID, @CurrentInstance, @Location, @StandbyLocation, @rn; WHILE @@ FETCH_STATUS -1 BEGIN - loại bỏ người dùng - đặt thành single_user rồi quay lại multi SET @sql =N'EXEC '+ QUOTENAME (@CurrentInstance) + N'. [Master] .sys.sp_executesql '+' N ' 'NẾU TỒN TẠI (CHỌN 1 TỪ sys.databases WHERE name =N' '' '' + @dbname + '' '' 'VÀ [trạng thái] 1) BẮT ĐẦU ALTER DATABASE' + QUOTENAME (@dbname) + N 'SET SINGLE_USER' + KHÔNG ĐƯỢC ROLLBACK NGAY LẬP TỨC; ALTER DATABASE '+ QUOTENAME (@dbname) + N' SET MULTI_USER; CHẤM DỨT;'';'; EXEC [master] .sys.sp_executesql @sql; - khôi phục nhật ký (trong STANDBY nếu là nhật ký cuối cùng):SET @sql =N'EXEC '+ QUOTENAME (@CurrentInstance) + N'. [master] .sys.sp_executesql '+ N'N''RESTORE LOG' + QUOTENAME (@dbname) + N 'FROM DISK =N' '' '' + @Location + N '' '' 'VỚI' + TRƯỜNG HỢP KHI @rn =1 VÀ (@CurrentInstance =@StandbyInstance HOẶC @PrepareAll =1) SAU ĐÓ N'STANDBY =N '' '' '+ @StandbyLocation + @dbname + N'.standby' '' '' ELSE N'NORECOVERY 'END + N'; ''; '; EXEC [master] .sys.sp_executesql @sql; - ghi lại thực tế là chúng tôi đã khôi phục nhật ký INSERT dbo.PMAG_LogRestoreHistory (DatabaseName, ServerInstance, BackupSetID, RestoreTime) SELECT @dbname, @CurrentInstance, @BackupSetID, SYSDATETIME (); - đánh dấu chế độ chờ mới NẾU @rn =1 VÀ @CurrentInstance =@StandbyInstance - đây là CẬP NHẬT STANDBY BEGIN mới dbo.PMAG_Secondaries SET IsCurrentStandby =CASE ServerInstance WHEN @StandbyInstance THEN 1 ELSE 0 END WHERE DatabaseName =@dbname; KẾT THÚC C VÀO @BackupSetID, @CurrentInstance, @Location, @StandbyLocation, @rn; KẾT THÚC ĐÓNG c; DEALLOCATE c; HẾT 

      (Tôi biết đó là rất nhiều mã và rất nhiều SQL động khó hiểu. Tôi đã cố gắng tỏ ra rất thoải mái với các nhận xét; nếu có phần nào bạn gặp khó khăn, vui lòng cho tôi biết.)

      Vì vậy, bây giờ, tất cả những gì bạn phải làm để khởi động và chạy hệ thống là thực hiện hai lệnh gọi thủ tục:

       EXEC dbo.PMAG_Backup @dbname =N'UserData ', @type =' bak ', @init =1; EXEC dbo.PMAG_Backup @dbname =N'UserData', @type ='trn'; 

      Bây giờ bạn sẽ thấy mỗi phiên bản có một bản sao dự phòng của cơ sở dữ liệu:

      Và bạn có thể xem cái nào hiện sẽ đóng vai trò là chế độ chờ chỉ đọc:

       CHỌN ServerInstance, IsCurrentStandby TỪ dbo.PMAG_Secondaries WHERE DatabaseName =N'UserData '; 

      Bước 6 - tạo công việc sao lưu / khôi phục nhật ký

      Bạn có thể đặt lệnh này vào công việc mà bạn lên lịch sau mỗi 15 phút:

       EXEC dbo.PMAG_Backup @dbname =N'UserData ', @type =' trn '; 

      Thao tác này sẽ thay đổi dữ liệu thứ cấp đang hoạt động sau mỗi 15 phút và dữ liệu của nó sẽ mới hơn 15 phút so với dữ liệu thứ cấp đang hoạt động trước đó. Nếu bạn có nhiều cơ sở dữ liệu trên các lịch trình khác nhau, bạn có thể tạo nhiều công việc hoặc lên lịch công việc thường xuyên hơn và kiểm tra dbo.PMAG_Databases bảng cho từng LogBackupFrequency_Minutes riêng lẻ giá trị để xác định xem bạn có nên chạy sao lưu / khôi phục cho cơ sở dữ liệu đó hay không.

      Bước 7 - xem và quy trình để cho ứng dụng biết chế độ chờ nào đang hoạt động

       TẠO CHẾ ĐỘ XEM dbo.PMAG_ActiveSecondariesAS CHỌN DatabaseName, ServerInstance TỪ dbo.PMAG_Secondaries WHERE IsCurrentStandby =1; ĐI TẠO QUY TRÌNH dbo.PMAG_GetActiveSecondary @dbname SYSNAMEASBEGIN ĐẶT SỐ TÀI KHOẢN BẬT; CHỌN ServerInstance FROM dbo.PMAG_ActiveSecondaries WHERE DatabaseName =@dbname; ENDGO 

      Trong trường hợp của tôi, tôi cũng đã tạo một chế độ xem hợp nhất theo cách thủ công trên tất cả UserData cơ sở dữ liệu để tôi có thể so sánh mức độ gần đây của dữ liệu trên dữ liệu chính với từng dữ liệu phụ.

       TẠO CHẾ ĐỘ XEM dbo.PMAG_CompareRecency_UserDataAS VỚI x (ServerInstance, EventTime) AS (SELECT @@ SERVERNAME, EventTime FROM UserData.dbo.LastUpdate UNION TẤT CẢ CHỌN N '. \ PEON1', EventTime FROM [. \ PEON1] .UserData.dbo .LastUpdate UNION ALL SELECT N '. \ PEON2', EventTime FROM [. \ PEON2] .UserData.dbo.LastUpdate UNION ALL SELECT N '. \ PEON3', EventTime FROM [. \ PEON3] .UserData.dbo.LastUpdate UNION ALL CHỌN N '. \ PEON4', EventTime FROM [. \ PEON4] .UserData.dbo.LastUpdate) CHỌN x.ServerInstance, s.IsCurrentStandby, x.EventTime, Age_Minutes =DATEDIFF (MINUTE, x.EventTime, SYSDATETIME ()), Age_Seconds =DATEDIFF (SECOND, x.EventTime, SYSDATETIME ()) FROM x LEFT OUTER JOIN dbo.PMAG_Secondaries AS s ON s.ServerInstance =x.ServerInstance AND s.DatabaseName =N'UserData '; GO 

      Kết quả lấy mẫu từ cuối tuần:

       SELECT [Now] =SYSDATETIME (); CHỌN ServerInstance, IsCurrentStandby, EventTime, Age_Minutes, Age_Seconds FROM dbo.PMAG_CompareRecency_UserData ORDER THEO Age_Seconds DESC; 

      Bước 8 - quy trình dọn dẹp

      Dọn dẹp lịch sử sao lưu và khôi phục nhật ký khá dễ dàng.

       TẠO THỦ TỤC dbo.PMAG_CleanupHistory @dbname SYSNAME, @DaysOld INT =7ASBEGIN BẬT TÀI KHOẢN BẬT; DECLARE @cutoff INT; - điều này giả định rằng một bản sao lưu nhật ký - thành công hoặc không thành công trên tất cả các thứ hai SELECT @cutoff =MAX (BackupSetID) FROM dbo.PMAG_LogBackupHistory AS bh WHERE DatabaseName =@dbname AND BackupTime  

      Bây giờ, bạn có thể thêm bước đó như một bước trong công việc hiện tại hoặc bạn có thể lên lịch hoàn toàn riêng biệt hoặc như một phần của các quy trình dọn dẹp khác.

      Tôi sẽ dọn dẹp hệ thống tệp cho một bài đăng khác (và có thể là một cơ chế hoàn toàn riêng biệt, chẳng hạn như PowerShell hoặc C # - đây thường không phải là điều bạn muốn T-SQL thực hiện).

      Bước 9 - tăng cường giải pháp

      Đúng là có thể có cách xử lý lỗi tốt hơn và các tính năng khác ở đây để làm cho giải pháp này hoàn thiện hơn. Hiện tại, tôi sẽ để đó như một bài tập cho người đọc, nhưng tôi dự định xem các bài đăng tiếp theo để biết chi tiết các cải tiến và cải tiến cho giải pháp này.

      Các biến và giới hạn

      Lưu ý rằng trong trường hợp của tôi, tôi đã sử dụng Standard Edition làm bản chính và Express Edition cho tất cả các bản thứ hai. Bạn có thể tiến thêm một bước trên quy mô ngân sách và thậm chí sử dụng Express Edition làm phiên bản chính - nhiều người nghĩ rằng Express Edition không hỗ trợ vận chuyển nhật ký, trong khi thực tế nó chỉ là trình hướng dẫn không có trong các phiên bản của Management Studio Express trước SQL Server 2012 Service Pack 1. Điều đó nói rằng, vì Express Edition không hỗ trợ SQL Server Agent, nên sẽ rất khó để biến nó thành nhà xuất bản trong trường hợp này - bạn sẽ phải định cấu hình bộ lập lịch của riêng mình để gọi các thủ tục được lưu trữ (C # ứng dụng dòng lệnh chạy bởi Windows Task Scheduler, PowerShell hoặc các công việc SQL Server Agent trên một phiên bản khác). Để sử dụng Express ở cả hai đầu, bạn cũng phải tự tin rằng tệp dữ liệu của mình sẽ không vượt quá 10GB và các truy vấn của bạn sẽ hoạt động tốt với các giới hạn về bộ nhớ, CPU và tính năng của phiên bản đó. Tôi hoàn toàn không gợi ý rằng Express là lý tưởng; Tôi chỉ sử dụng nó để chứng minh rằng có thể có các tạp chí thứ hai rất linh hoạt có thể đọc được miễn phí (hoặc rất gần với nó).

      Ngoài ra, các trường hợp riêng biệt này trong kịch bản của tôi đều hoạt động trên cùng một máy ảo, nhưng nó hoàn toàn không phải hoạt động theo cách đó - bạn có thể trải các trường hợp ra trên nhiều máy chủ; hoặc, bạn có thể làm theo cách khác và khôi phục về các bản sao khác nhau của cơ sở dữ liệu, với các tên khác nhau, trên cùng một phiên bản. Những cấu hình này sẽ yêu cầu những thay đổi tối thiểu đối với những gì tôi đã trình bày ở trên. Và bạn khôi phục bao nhiêu cơ sở dữ liệu và tần suất ra sao là hoàn toàn tùy thuộc vào bạn - mặc dù sẽ có giới hạn trên thực tế (trong đó [thời gian truy vấn trung bình]> [số thứ hai] x [khoảng thời gian sao lưu nhật ký] ).

      Cuối cùng, chắc chắn có một số hạn chế với cách tiếp cận này. Danh sách không đầy đủ:

      1. Mặc dù bạn có thể tiếp tục sao lưu đầy đủ theo lịch trình của riêng mình, nhưng các bản sao lưu nhật ký phải đóng vai trò là cơ chế sao lưu nhật ký duy nhất của bạn. Nếu bạn cần lưu trữ các bản sao lưu nhật ký cho các mục đích khác, bạn sẽ không thể sao lưu nhật ký riêng biệt với giải pháp này, vì chúng sẽ can thiệp vào chuỗi nhật ký. Thay vào đó, bạn có thể cân nhắc thêm MIRROR TO bổ sung arguments to the existing log backup scripts, if you need to have copies of the logs used elsewhere.
      2. While "Poor Man's Availability Groups" may seem like a clever name, it can also be a bit misleading. This solution certainly lacks many of the HA/DR features of Availability Groups, including failover, automatic page repair, and support in the UI, Extended Events and DMVs. This was only meant to provide the ability for non-Enterprise customers to have an infrastructure that supports multiple readable secondaries.
      3. I tested this on a very isolated VM system with no concurrency. This is not a complete solution and there are likely dozens of ways this code could be made tighter; as a first step, and to focus on the scaffolding and to show you what's possible, I did not build in bulletproof resiliency. You will need to test it at your scale and with your workload to discover your breaking points, and you will also potentially need to deal with transactions over linked servers (always fun) and automating the re-initialization in the event of a disaster.

      The "Insurance Policy"

      Log shipping also offers a distinct advantage over many other solutions, including Availability Groups, mirroring and replication:a delayed "insurance policy" as I like to call it. At my previous job, I did this with full backups, but you could easily use log shipping to accomplish the same thing:I simply delayed the restores to one of the secondary instances by 24 hours. This way, I was protected from any client "shooting themselves in the foot" going back to yesterday, and I could get to their data easily on the delayed copy, because it was 24 hours behind. (I implemented this the first time a customer ran a delete without a where clause, then called us in a panic, at which point we had to restore their database to a point in time before the delete – which was both tedious and time consuming.) You could easily adapt this solution to treat one of these instances not as a read-only secondary but rather as an insurance policy. More on that perhaps in another post.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. 50 sắc thái của NULL - Ý nghĩa khác nhau của NULL trong SQL

  2. Một cải tiến tiềm năng cho các bản cập nhật thống kê:MAXDOP

  3. Sắp xếp thứ tràn đến cấp độ 15.000

  4. Cải thiện hiệu suất cơ sở dữ liệu lên 400%

  5. 12 Toán tử SQL thường được sử dụng