Статьи

Использование SQL Azure Federations через PHP

Среди декабрьских обновлений для платформы Windows Azure было введение федераций SQL Azure . В двух словах, в федерациях SQL Azure представлен уровень абстракции для разделения баз данных SQL Azure. Ценность в федерациях заключается в вашей способности иметь эластичную масштабируемость уровня базы данных вашего приложения (чтобы соответствовать эластичной масштабируемости остальной части вашего приложения, когда оно работает в облаке). И одна хорошая вещь о том, как работают федерации, состоит в том, что почти все можно сделать с помощью простых команд SQL. Конечно, это означает, что использование SQL Azure Federations через PHP должно быть простым. Итак, в этом посте я познакомлю вас с федерациями SQL Azure и покажу, как их использовать через PHP.

Обратите внимание, что я буду использовать драйверы SQL Server для PHP для подключения к SQL Azure. Вы можете скачать драйверы здесь: http://www.microsoft.com/download/en/details.aspx?id=20098 . Документация для драйверов находится здесь: http://msdn.microsoft.com/en-us/library/ee229547(SQL.10).aspx .

Создайте сервер SQL Azure

Создание сервера SQL Azure — это единственный шаг, который вы не можете сделать с помощью PHP, и я не могу сделать ничего лучше, чем эта статья « Начало работы: создание сервера и базы данных SQL Azure» , которая покажет вам, как это сделать (просто Выполните задачу 1, хотя… я проведу вас через задачу 2 (Создание базы данных) с помощью PHP.) Запишите 10-символьный идентификатор сервера и имя пользователя / пароль для вашего сервера… эта информация понадобится вам в коде ниже ,

Создать базу данных

Вы можете создать базу данных SQL Azure через портал разработчика (как показано в учебном руководстве, указанном выше), но вы также можете сделать это через PHP. Вот как это сделать (я думаю, что большая часть этого кода говорит сама за себя, но позже я добавлю некоторые комментарии):

$serverName = "tcp:SERVERID.database.windows.net, 1433"; 
$connectionOptions = array("Database" => "master", "UID" => "USER@SERVERID", "PWD" => "PASSWORD");
 

$conn = sqlsrv_connect($serverName, $connectionOptions);
if($conn === false)      
    die(print_r(sqlsrv_errors(), true)); 
else
    echo "Connected via sqlsrv!<br />";
 

// Create database (be sure to connect to master)
$sql = "CREATE DATABASE SalesDB";
$stmt = sqlsrv_query($conn, $sql);
if($stmt === false)
    die(print_r(sqlsrv_errors(), true)); 
else
    echo "SalesDB database created!<br />";

Я думаю, что в приведенном выше коде важно отметить детали подключения. Обратите внимание, что $ serverName указывает и протокол (tcp), и порт (1433). SERVERID этого имя 10-символьным ваш сервер SQL Azure, который необходим как часть имени сервера ( SERVERID.database.windows.net ) и как часть имени пользователя ( USER @ идентификатор_сервер ) в $ connectionOptions массиве. Также обратите внимание, что я подключаюсь к основной базе данных. Кроме того, создание базы данных SQL Azure аналогично созданию базы данных SQL Server.

Если вы хотите убедиться, что SalesDB была фактически создана, вы можете сделать это, войдя на портал разработчика , выбрав База данных и щелкнув по вашему серверу. Вот что вы должны увидеть:

образ

Создать федерацию

Следующим шагом является создание федерации SQL Azure. Что такое федерация? Полная информация здесь , но краткое описание — это то, что Федерация — это объект базы данных, который управляет большей частью сложности, которая обычно возникает при реализации шардинга. Создать федерацию просто … обратите внимание, что вся «магия» в SQL:

$serverName = "tcp:SERVERID.database.windows.net, 1433"; 
$connectionOptions = array("Database" => "SalesDB", "UID" => "USER@SERVERID", "PWD" => "PASSWORD");
 

$conn = sqlsrv_connect($serverName, $connectionOptions);
if($conn === false)      
    die(print_r(sqlsrv_errors(), true)); 
else
    echo "Connected via sqlsrv!<br />";
 

// Create federation Orders_Federation (be sure to connect to SalesDB)
$sql = "CREATE FEDERATION Orders_Federation (CustId INT RANGE)";
$stmt = sqlsrv_query($conn, $sql);
if($stmt === false)
    die(print_r(sqlsrv_errors()));
else
    echo "Orders_Federation created!<br />";

Обратите внимание, что я указываю SalesDB в качестве базы данных в моих $ conectionOptions .

CustID в $ SQL строка определяет ключ распределения федерации , который определяет распределение данных к разделам внутри федерации. Ключ распределения федерации должен быть INT, BIGINT, UNIQUEIDENTIFIER или VARBINARY (до 900 байтов). RANGE в запросе указывает тип разбиения. Для получения более подробной информации см. СОЗДАНИЕ ФЕДЕРАЦИИ .

Просмотр членов федерации

Выполнение запроса выше создает вашего первого члена федерации (подумайте «первый осколок»). Таким образом, теперь у вас есть корневая база данных (SalesDB) и один член федерации, имя которого непрозрачно (что является точкой в ​​федерациях … вам не нужно знать имена баз данных федерации). Однако вы можете получить информацию об участнике, выполнив этот код (с тем же кодом подключения, что и в примере выше):

$sql = "SELECT federation_id, 
               member_id, 
               distribution_name, 
               CAST(range_low AS INT) AS range_low, 
               CAST(range_high AS INT) AS range_high 
        FROM sys.federation_member_distributions";
$stmt = sqlsrv_query($conn, $sql);
if($stmt === false)
    die(print_r(sqlsrv_errors()));
else
{
    echo "Federation members retrieved!<br />";
    while($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC))
    {
        echo "<pre>";
        print_r($row);
        echo "</pre>";
    }
}

Это станет более интересным, когда мы разделим федерацию (подробности ниже). Я вернусь к этому позже.

Создать федеративные таблицы

Следующим шагом является создание таблиц в нашей корневой базе данных и членах федерации. Следующий код подключается к нашему объекту федерации и делает это. Обязательно прочитайте заметки, которые следуют примеру:

$serverName = "tcp:SERVERID.database.windows.net, 1433"; 

$connectionOptions = array("Database" => "SalesDB", "UID" => "USER@SERVERID", "PWD" => "PASSWORD", "MultipleActiveResultSets" => false);

 
$conn = sqlsrv_connect($serverName, $connectionOptions);
if($conn === false)      
    die(print_r(sqlsrv_errors(), true)); 
else
    echo "Connected via sqlsrv!<br />";
 
// Create tables
$sql1 = "USE FEDERATION Orders_Federation (CustId = 0) WITH RESET, FILTERING = OFF;";
$stmt = sqlsrv_query($conn, $sql1);
if($stmt === false)
    die(print_r(sqlsrv_errors()));
else
    echo "Connected to Orders_Federation!<br />";
    
$sql2 = "-- Create Products table
        CREATE TABLE Products(
          ProductID int NOT NULL,
          SupplierID int NOT NULL,
          ProductName nvarchar(50) NOT NULL,
          Price decimal(12,2) NOT NULL,
          PRIMARY KEY(ProductId) )
 
        -- Create Customers table
        CREATE TABLE Customers(
          CustomerID int NOT NULL,
          CompanyName nvarchar(50) NOT NULL,
          FirstName nvarchar(50),
          LastName nvarchar(50),
          PRIMARY KEY (CustomerId) )
        FEDERATED ON (CustId = CustomerID)
 
        -- Create Orders table
        create table Orders 
        (
               CustomerId int NOT NULL,
               OrderId int NOT NULL,
               OrderDate datetime,
               PRIMARY KEY (OrderId, CustomerId)
        )
        FEDERATED ON (CustId = CustomerId)


        -- Create OrderItems table
        CREATE TABLE OrderItems(
          CustomerID int NOT NULL,
          OrderID int NOT NULL,
          ProductID int NOT NULL,
          Quantity int NOT NULL,
          PRIMARY KEY (OrderId, CustomerId, ProductId) )
        FEDERATED ON (CustId = CustomerId)";
$stmt = sqlsrv_query($conn, $sql2);
if($stmt === false)
    die(print_r(sqlsrv_errors()));
else
    echo "Tables created!<br />";

Примечания к фрагменту кода выше:

  1. Обратите внимание, что в массиве $ connectionOptions MultipleActiveResultSets отключен. Это необходимо для выполнения запроса.
  2. Два запроса выполняются. Первый подключается к нашей федерации, а второй создает таблицы. Попытка выполнить оба этих запроса в виде одного пакета приведет к ошибке ( USE FEDERATION не может быть частью пакетного запроса).
  3. CustID = 0 в ИСПОЛЬЗОВАНИЕ ФЕДЕРАЦИИ запроса соединяет нас к члену федерации , содержащей CustID = 0 . Это только синтаксически необходимо здесь.
  4. Поскольку таблица «Продукты» создается без предложения FEDERATED ON , она создается в корневой базе данных, а не в членах федерации. (В этом примере я ожидаю, что мои другие таблицы будут расти, следовательно, они объединены. Я не ожидаю, что мои продукты будут расти так быстро, что потребует объединения).

Вставить данные

После того, как мы создали объект федерации и член федерации, вставка данных практически такая же, как и для SQL Server. Разница лишь в том, что нам нужно сначала подключиться к члену федерации. (Примечание № 2 выше относится и к этому примеру кода.)

$sql1 = "USE FEDERATION Orders_Federation (CustId = 0) WITH RESET, FILTERING = OFF;";
$stmt = sqlsrv_query($conn, $sql1);
if($stmt === false)
    die(print_r(sqlsrv_errors()));
else
    echo "Connected to Orders_Federation!<br />";
    
$sql2 = "INSERT INTO Products (ProductID, SupplierID, ProductName, Price)
        VALUES ( 386, 1001,    'Titanium Extension Bracket Left Hand',    5.25 )
        
        INSERT INTO Products (ProductID, SupplierID, ProductName, Price)
        VALUES ( 387, 1001,    'Titanium Extension Bracket Right Hand', 5.25 )
        
        INSERT INTO Products (ProductID, SupplierID, ProductName, Price)
        VALUES ( 388, 1001,    'Fusion Generator Module 5 kV',    10.50 )
        
        INSERT INTO Products (ProductID, SupplierID, ProductName, Price)
        VALUES ( 389, 1001,    'Bypass Filter 400 MHz Low Pass', 10.50 )
        
 
        INSERT INTO Customers (CustomerID, CompanyName, FirstName, LastName)
        VALUES (10, 'Van Nuys', 'Catherine', 'Abel')
        
        INSERT INTO Customers (CustomerID, CompanyName, FirstName, LastName)
        VALUES (20, 'Abercrombie', 'Kim', 'Branch')
        
        INSERT INTO Customers (CustomerID, CompanyName, FirstName, LastName)
        VALUES (30, 'Contoso', 'Frances', 'Adams')
        
        INSERT INTO Customers (CustomerID, CompanyName, FirstName, LastName)
        VALUES (40, 'A. Datum Corporation', 'Mark', 'Harrington')
        
        INSERT INTO Customers (CustomerID, CompanyName, FirstName, LastName)
        VALUES (50, 'Adventure Works', 'Keith', 'Harris')
        
        INSERT INTO Customers (CustomerID, CompanyName, FirstName, LastName)
        VALUES (60, 'Alpine Ski House', 'Wilson', 'Pais')
        
        INSERT INTO Customers (CustomerID, CompanyName, FirstName, LastName)
        VALUES (70, 'Baldwin Museum of Science', 'Roger', 'Harui')
        
        INSERT INTO Customers (CustomerID, CompanyName, FirstName, LastName)
        VALUES (80, 'Blue Yonder Airlines', 'Pilar', 'Pinilla')
        
        INSERT INTO Customers (CustomerID, CompanyName, FirstName, LastName)
        VALUES (90, 'City Power & Light', 'Kari', 'Hensien')
        
        INSERT INTO Customers (CustomerID, CompanyName, FirstName, LastName)
        VALUES (100, 'Coho Winery', 'Peter', 'Brehm')

        

        DECLARE @orderId INT
        DECLARE @customerId INT
 
        SET @orderId = 10
        SELECT @customerId = CustomerId FROM Customers WHERE LastName = 'Hensien' and FirstName = 'Kari'
 
        INSERT INTO Orders (CustomerId, OrderId, OrderDate)
        VALUES (@customerId, @orderId, GetDate())
 
        INSERT INTO OrderItems (CustomerID, OrderID, ProductID, Quantity)
        VALUES (@customerId, @orderId, 388, 4)
 
        SET @orderId = 20
        SELECT @customerId = CustomerId FROM Customers WHERE LastName = 'Harui' and FirstName = 'Roger'
 
        INSERT INTO Orders (CustomerId, OrderId, OrderDate)
        VALUES (@customerId, @orderId, GetDate())
 
        INSERT INTO OrderItems (CustomerID, OrderID, ProductID, Quantity)
        VALUES (@customerId, @orderId, 389, 2)
 
        SET @orderId = 30
        SELECT @customerId = CustomerId FROM Customers WHERE LastName = 'Brehm' and FirstName = 'Peter'
 
        INSERT INTO Orders (CustomerId, OrderId, OrderDate)
        VALUES (@customerId, @orderId, GetDate())
 
        INSERT INTO OrderItems (CustomerID, OrderID, ProductID, Quantity)
        VALUES (@customerId, @orderId, 387, 3)
 
        SET @orderId = 40
        SELECT @customerId = CustomerId FROM Customers WHERE LastName = 'Pais' and FirstName = 'Wilson'
 
        INSERT INTO Orders (CustomerId, OrderId, OrderDate)
        VALUES (@customerId, @orderId, GetDate())
 
        INSERT INTO OrderItems (CustomerID, OrderID, ProductID, Quantity)
        VALUES (@customerId, @orderId, 388, 1)
        ";

$stmt2 = sqlsrv_query($conn, $sql2);
if($stmt2 === false)
    die(print_r(sqlsrv_errors()));
else
    echo "Data inserted. <br />";

Код подключения для приведенного выше примера такой же, как в разделе «Создание федеративных таблиц».

Разделить федерацию

Вот где действительно начинает показывать ценность SQL Azure Federations. Теперь, когда мой первый член федерации начинает заполняться данными, я могу выполнить запрос, который создаст второго члена федерации и переместит данные из первого члена во второй. В приведенном ниже примере я перемещаю все данные с CustId> = 60 во вторую федерацию:

Разделение занимает несколько минут. Если вы подождете несколько минут после выполнения запроса, а затем выполните запрос в разделе «Просмотр элементов федерации» выше, вы должны увидеть что-то вроде этого:

образ

Как видите, в нашей федерации сейчас два члена. Первый член содержит данные с CustId, которые идут от нижней части диапазона INT до (но не включая) 60, а второй член содержит CustId> = 60. По мере роста наших данных мы можем выполнять больше команд разделения для объединения наших данных. через больше членов.

Вставка данных после разделения

Логический вопрос, который нужно задать после разделения федерации: «Как вставить данные в мои федеративные таблицы?» Ответ прост: сгенерируйте новый ключ распространения, подключитесь к соответствующему члену федерации и вставьте. К счастью, легко подключиться к соответствующему члену федерации: просто укажите CustId = (новый Id) в запросе USE FEDERATION . Таким образом, добавление нового клиента, заказа и позиции заказа с CustId = 55 может выглядеть примерно так:

$sql1 = "USE FEDERATION Orders_Federation (CustId = 55) WITH RESET, FILTERING = OFF;";
$stmt = sqlsrv_query($conn, $sql1);
if($stmt === false)
    die(print_r(sqlsrv_errors()));
else
    echo "Connected to Orders_Federation!<br />";
 
$params2 = array(55, 37);    
$sql2 = "DECLARE @customerId INT
        SET @customerId = ?
        INSERT INTO Customers (CustomerID, CompanyName, FirstName, LastName)
        VALUES (@customerId, 'Microsoft', 'Swan', 'Brian')
 
        DECLARE @orderId INT
        SET @orderId = ?
        
        INSERT INTO Orders (CustomerId, OrderId, OrderDate)
        VALUES (@customerId, @orderId, GetDate())
 
        INSERT INTO OrderItems (CustomerID, OrderID, ProductID, Quantity)
        VALUES (@customerId, @orderId, 389, 1)";
        
$stmt2 = sqlsrv_query($conn, $sql2, $params2);
if($stmt2 === false)
    die(print_r(sqlsrv_errors()));
else
    echo "Data inserted. <br />";

Чтобы вернуть эти данные, см. Раздел « Запрос члена федерации с включенной фильтрацией » ниже.

Как видите, выбор ключа распределения федерации, который можно генерировать случайным образом, важен для обеспечения равномерного распределения данных между членами федерации.

Запросить члена федерации с отключенной фильтрацией

Чтобы получить данные, мы (снова) подключаемся к члену федерации. Чтобы определить, к какому члену мы подключаемся, мы указываем значение для ключа распределения федерации. В приведенном ниже примере в запросе USE FEDERATION указано CustId = 0 , поэтому мы подключены к члену, диапазон которого содержит CustId 0. Поскольку в запросе FILTERING = OFF, возвращаются все данные от члена федерации (т. Е. данные не фильтруются указанным CustId). в этом случае все данные из таблицы Customers возвращаются указанным членом федерации:

// Query federation member
$sql1 = "USE FEDERATION Orders_Federation (CustId = 0) WITH RESET, FILTERING = OFF;";
$stmt1 = sqlsrv_query($conn, $sql1);
if($stmt1 === false)
    die(print_r(sqlsrv_errors()));
else
    echo "Connected to Orders_Federation!<br />";
    
$sql2 = "SELECT * FROM Customers";
$stmt2 = sqlsrv_query($conn, $sql2);
if($stmt2 === false)
    die(print_r(sqlsrv_errors()));
else
    echo "Data retrieved. <br />";
 
while($row = sqlsrv_fetch_array($stmt2, SQLSRV_FETCH_ASSOC))
{
    echo "<pre>";
    print_r($row);
    echo "</pre>";
}

Код подключения для этого примера такой же, как в примере создания федеративных таблиц .

Запрос члена федерации с включенной фильтрацией

В этом примере я подключусь ко второму члену федерации, указав CustId, который попадает в диапазон для этого члена. Я также установлю FILTERING = ON при подключении к члену федерации. Это отфильтрует возвращаемые данные по указанному CustId, поэтому мы вернем только данные, связанные с CustId = 90. (Если мы установим FILTERING = OFF, мы получим все данные от члена федерации).

$serverName = "tcp:SERVERID.database.windows.net, 1433"; 
$connectionOptions = array("Database" => "SalesDB", "UID" => "USER@SERVERID", "PWD" => "PASSWORD", "MultipleActiveResultSets" => false, "ReturnDatesAsStrings" => true);
 

$conn = sqlsrv_connect($serverName, $connectionOptions);
if($conn === false)      
    die(print_r(sqlsrv_errors(), true)); 
else
    echo "Connected via sqlsrv!<br />";
 
$sql1 = "USE FEDERATION Orders_Federation (CustId = 90) WITH RESET, FILTERING = ON;";
$stmt1 = sqlsrv_query($conn, $sql1);
if($stmt1 === false)
    die(print_r(sqlsrv_errors()));
else
    echo "Connected to Orders_Federation!<br />";
    
$sql2 = "SELECT * FROM Customers;
         SELECT * FROM Orders;
         SELECT * FROM OrderItems;";
         
$stmt2 = sqlsrv_query($conn, $sql2);
if($stmt2 === false)
    die(print_r(sqlsrv_errors()));
else
    echo "Data retrieved. <br />";
 
do{
    while($row = sqlsrv_fetch_array($stmt2, SQLSRV_FETCH_ASSOC))
    {
        echo "<pre>";
        print_r($row);
        echo "</pre>";
    }
}while(sqlsrv_next_result($stmt2));

Обратите внимание, что я немного изменил код подключения: ReturnDatesAsStrings установлен в true в $ connectionOptions .

Вот вывод:

образ

 

Дополнительные замечания

Одним из потенциальных недостатков текущей реализации федераций SQL Azure является отсутствие простого способа сокращения членов федерации. Как видите, добавить участников федерации и распределить данные по ним легко, но в настоящее время не так просто объединить данные в меньшее количество членов федерации. Команда SQL Azure ищет способы упростить масштабирование.

Дополнительные ресурсы

Начало работы с PHP и SQL Azure

Введение в разветвленные запросы

http://blogs.msdn.com/b/cbiyikoglu/

Справочник по Transact-SQL (база данных SQL Azure)

Вот и все пока … просто введение в федерации SQL Azure, действительно. Чтобы получить более глубокий взгляд, ознакомьтесь с некоторыми из дополнительных ресурсов выше.

Спасибо.

-Брайан

 

Источник: http://blogs.msdn.com/b/silverlining/archive/2012/01/18/using-sql-azure-federations-via-php.aspx