教學課程:實作異地分散式資料庫 (Azure SQL 資料庫)

適用於:Azure SQL Database

在 SQL 資料庫和用戶端應用程式中設定資料庫,以故障轉移至遠端區域並測試故障轉移計劃。 您將學習如何:

  • 建立容錯移轉群組
  • 執行 Java 應用程式以在 SQL 資料庫中查詢資料庫
  • 測試容錯移轉

如果您沒有 Azure 訂用帳戶,請在開始前建立免費帳戶

必要條件

注意

本文使用 Azure Az PowerShell 模組,這是與 Azure 互動時建議使用的 PowerShell 模組。 若要開始使用 Az PowerShell 模組,請參閱安裝 Azure PowerShell。 若要瞭解如何遷移至 Az PowerShell 模組,請參閱將Migrate Azure PowerShell 從 AzureRM 遷移至 Az

重要

Azure SQL Database 仍然支援 PowerShell Azure Resource Manager 模組,但所有未來的開發都是針對 Az.Sql 模組。 如需這些 Cmdlet,請參閱 AzureRM.Sql \(英文\)。 Az 模組和 AzureRm 模組中命令的引數本質上完全相同。

若要完成本教學課程,請確定您已安裝下列必要項目:

重要

請務必設定防火牆規則,以使用您在本教學課程中執行步驟的計算機公用IP位址。 資料庫層級防火牆規則會自動復寫到輔助伺服器。

如需詳細資訊,請參閱 建立資料庫層級防火牆規則 ,或判斷計算機伺服器層級防火牆規則所使用的IP位址,請參閱 建立伺服器層級防火牆

建立容錯移轉群組

在此步驟中,您將在現有的伺服器與另一個區域中的新伺服器之間建立 容錯移轉群組。 然後將範例資料庫新增到容錯移轉群組。

重要

此範例需要 Azure PowerShell Az 1.0 或更新版本。 執行 Get-Module -ListAvailable Az 可查看已安裝的版本。 如果您需要安裝,請參閱安裝 Azure PowerShell 模組

執行 Connect-AzAccount 來登入 Azure。

若要建立故障轉移群組,請執行下列腳本:

$admin = "<adminName>"
$password = "<password>"
$resourceGroup = "<resourceGroupName>"
$location = "<resourceGroupLocation>"
$server = "<serverName>"
$database = "<databaseName>"
$drLocation = "<disasterRecoveryLocation>"
$drServer = "<disasterRecoveryServerName>"
$failoverGroup = "<globallyUniqueFailoverGroupName>"

# create a backup server in the failover region
New-AzSqlServer -ResourceGroupName $resourceGroup -ServerName $drServer `
    -Location $drLocation -SqlAdministratorCredentials $(New-Object -TypeName System.Management.Automation.PSCredential `
    -ArgumentList $admin, $(ConvertTo-SecureString -String $password -AsPlainText -Force))

# create a failover group between the servers
New-AzSqlDatabaseFailoverGroup –ResourceGroupName $resourceGroup -ServerName $server `
    -PartnerServerName $drServer –FailoverGroupName $failoverGroup –FailoverPolicy Automatic -GracePeriodWithDataLossHours 2

# add the database to the failover group
Get-AzSqlDatabase -ResourceGroupName $resourceGroup -ServerName $server -DatabaseName $database | `
    Add-AzSqlDatabaseToFailoverGroup -ResourceGroupName $resourceGroup -ServerName $server -FailoverGroupName $failoverGroup

異地復寫設定也可以在 Azure 入口網站 中變更,方法是選取您的資料庫,然後 設定> Geo-Replication。

Geo-replication settings

取得範例專案

  1. 在控制台中,使用下列命令建立 Maven 專案:

    mvn archetype:generate "-DgroupId=com.sqldbsamples" "-DartifactId=SqlDbSample" "-DarchetypeArtifactId=maven-archetype-quickstart" "-Dversion=1.0.0"
    
  2. 輸入 Y,然後按 Enter

  3. 將目錄 () 變更為專案的資料夾。

    cd SqlDbSample
    
  4. 使用您慣用的編輯器,在專案資料夾中開啟 pom.xml 檔案。

  5. 新增下列 dependency 區段,以新增 Microsoft JDBC Driver for SQL Server 相依性。 相依性必須貼在較大的 dependencies 區段內。

    <dependency>
      <groupId>com.microsoft.sqlserver</groupId>
      <artifactId>mssql-jdbc</artifactId>
     <version>6.1.0.jre8</version>
    </dependency>
    
  6. dependencies 區段之後新增 properties 區段,以指定 Java 版本:

    <properties>
      <maven.compiler.source>1.8</maven.compiler.source>
      <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
    
  7. build 區段之後新增 properties 區段,以支援指令清單檔:

    <build>
      <plugins>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-jar-plugin</artifactId>
          <version>3.0.0</version>
          <configuration>
            <archive>
              <manifest>
                <mainClass>com.sqldbsamples.App</mainClass>
              </manifest>
            </archive>
         </configuration>
        </plugin>
      </plugins>
    </build>
    
  8. 儲存並關閉 pom.xml 檔案。

  9. 開啟位於 .的App.java檔案。\SqlDbSample\src\main\java\com\sqldbsamples,並以下列程序代碼取代內容:

    package com.sqldbsamples;
    
    import java.sql.Connection;
    import java.sql.Statement;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.Timestamp;
    import java.sql.DriverManager;
    import java.util.Date;
    import java.util.concurrent.TimeUnit;
    
    public class App {
    
       private static final String FAILOVER_GROUP_NAME = "<your failover group name>";  // add failover group name
    
       private static final String DB_NAME = "<your database>";  // add database name
       private static final String USER = "<your admin>";  // add database user
       private static final String PASSWORD = "<your password>";  // add database password
    
       private static final String READ_WRITE_URL = String.format("jdbc:" +
          "sqlserver://%s.database.windows.net:1433;database=%s;user=%s;password=%s;encrypt=true;" +
          "hostNameInCertificate=*.database.windows.net;loginTimeout=30;",
          FAILOVER_GROUP_NAME, DB_NAME, USER, PASSWORD);
       private static final String READ_ONLY_URL = String.format("jdbc:" +
          "sqlserver://%s.secondary.database.windows.net:1433;database=%s;user=%s;password=%s;encrypt=true;" +
          "hostNameInCertificate=*.database.windows.net;loginTimeout=30;",
          FAILOVER_GROUP_NAME, DB_NAME, USER, PASSWORD);
    
       public static void main(String[] args) {
          System.out.println("#######################################");
          System.out.println("## GEO DISTRIBUTED DATABASE TUTORIAL ##");
          System.out.println("#######################################");
          System.out.println("");
    
          int highWaterMark = getHighWaterMarkId();
    
          try {
             for(int i = 1; i < 1000; i++) {
                 //  loop will run for about 1 hour
                 System.out.print(i + ": insert on primary " +
                    (insertData((highWaterMark + i)) ? "successful" : "failed"));
                 TimeUnit.SECONDS.sleep(1);
                 System.out.print(", read from secondary " +
                    (selectData((highWaterMark + i)) ? "successful" : "failed") + "\n");
                 TimeUnit.SECONDS.sleep(3);
             }
          } catch(Exception e) {
             e.printStackTrace();
       }
    }
    
    private static boolean insertData(int id) {
       // Insert data into the product table with a unique product name so we can find the product again
       String sql = "INSERT INTO SalesLT.Product " +
          "(Name, ProductNumber, Color, StandardCost, ListPrice, SellStartDate) VALUES (?,?,?,?,?,?);";
    
       try (Connection connection = DriverManager.getConnection(READ_WRITE_URL);
               PreparedStatement pstmt = connection.prepareStatement(sql)) {
          pstmt.setString(1, "BrandNewProduct" + id);
          pstmt.setInt(2, 200989 + id + 10000);
          pstmt.setString(3, "Blue");
          pstmt.setDouble(4, 75.00);
          pstmt.setDouble(5, 89.99);
          pstmt.setTimestamp(6, new Timestamp(new Date().getTime()));
          return (1 == pstmt.executeUpdate());
       } catch (Exception e) {
          return false;
       }
    }
    
    private static boolean selectData(int id) {
       // Query the data previously inserted into the primary database from the geo replicated database
       String sql = "SELECT Name, Color, ListPrice FROM SalesLT.Product WHERE Name = ?";
    
       try (Connection connection = DriverManager.getConnection(READ_ONLY_URL);
               PreparedStatement pstmt = connection.prepareStatement(sql)) {
          pstmt.setString(1, "BrandNewProduct" + id);
          try (ResultSet resultSet = pstmt.executeQuery()) {
             return resultSet.next();
          }
       } catch (Exception e) {
          return false;
       }
    }
    
    private static int getHighWaterMarkId() {
       // Query the high water mark id stored in the table to be able to make unique inserts
       String sql = "SELECT MAX(ProductId) FROM SalesLT.Product";
       int result = 1;
       try (Connection connection = DriverManager.getConnection(READ_WRITE_URL);
               Statement stmt = connection.createStatement();
               ResultSet resultSet = stmt.executeQuery(sql)) {
          if (resultSet.next()) {
              result =  resultSet.getInt(1);
             }
          } catch (Exception e) {
           e.printStackTrace();
          }
          return result;
       }
    }
    
  10. 儲存並關閉 App.java 檔案。

  11. 在主控台中,執行下列命令:

    mvn package
    
  12. 啟動將執行約 1 小時到手動停止的應用程式,讓您有時間執行故障轉移測試。

    mvn -q -e exec:java "-Dexec.mainClass=com.sqldbsamples.App"
    
    #######################################
    ## GEO DISTRIBUTED DATABASE TUTORIAL ##
    #######################################
    
    1. insert on primary successful, read from secondary successful
    2. insert on primary successful, read from secondary successful
    3. insert on primary successful, read from secondary successful
    ...
    

測試容錯移轉

執行下列腳本來模擬故障轉移並觀察應用程式結果。 請注意,在資料庫移轉期間,某些插入和選取作業會如何失敗。

您可以使用下列命令,在測試期間檢查災害復原伺服器的角色:

(Get-AzSqlDatabaseFailoverGroup -FailoverGroupName $failoverGroup `
    -ResourceGroupName $resourceGroup -ServerName $drServer).ReplicationRole

執行測試容錯移轉:

  1. 啟動故障轉移群組的手動故障轉移:

    Switch-AzSqlDatabaseFailoverGroup -ResourceGroupName $resourceGroup `
     -ServerName $drServer -FailoverGroupName $failoverGroup
    
  2. 將容錯移轉群組還原至主要伺服器:

    Switch-AzSqlDatabaseFailoverGroup -ResourceGroupName $resourceGroup `
     -ServerName $server -FailoverGroupName $failoverGroup
    

下一步

查閱 高可用性和災害復原檢查清單

相關的 Azure SQL 資料庫內容: