如何通过 PHP 使用 Azure 存储表服务

Tip

本文中的内容适用于原始的 Azure 表存储。

概述

本指南演示如何使用 Azure 存储表服务 执行常见方案。 示例采用 PHP 编写,并使用了 Azure 存储表 PHP 客户端库。 涉及的方案包括创建和删除表以及在表中插入、删除和查询实体。 有关 Azure 表服务的详细信息,请参阅后续步骤部分。

什么是表存储

Azure 表存储可存储大量结构化数据。 该服务是一个 NoSQL 数据存储,接受来自 Azure 云内部和外部的通过验证的呼叫。 Azure 表最适合存储结构化非关系型数据。 表存储的常见用途包括:

  • 存储 TB 量级的结构化数据,能够为 Web 规模应用程序提供服务
  • 存储无需复杂联接、外键或存储过程,并且可以对其进行非规范化以实现快速访问的数据集
  • 使用聚集索引快速查询数据
  • 使用 OData 协议和 LINQ 查询以及 WCF 数据服务 .NET 库访问数据

可以使用表存储来存储和查询大型结构化非关系型数据集,并且表会随着需求的增加而扩展。

表存储概念

表存储包含以下组件:

表存储组件图

  • URL 格式:Azure 表存储帐户使用此格式:http://<storage account>.table.core.chinacloudapi.cn/<table>

    可以直接使用此地址和 OData 协议来访问 Azure 表。 有关详细信息,请参阅 OData.org

  • 帐户:对 Azure 存储进行的所有访问都要通过存储帐户完成。 有关存储帐户容量的详细信息,请参阅 Azure 存储可伸缩性和性能目标
  • :表是实体的集合。 表不对实体强制实施架构,这意味着单个表可以包含具有不同属性集的实体。
  • 实体:与数据库行类似,一个实体就是一组属性。 Azure 存储中的实体大小最大可以为 1MB。
  • 属性:属性是名称/值对。 每个实体最多可包含 252 个用于存储数据的属性。 每个实体还具有三个系统属性,分别指定分区键、行键和时间戳。 对具有相同分区键的实体的查询速度将更快,并且可以在原子操作中插入/更新这些实体。 一个实体的行键是它在一个分区内的唯一标识符。

有关命名表和属性的详细信息,请参阅 了解表服务数据模型

创建 Azure 服务帐户

可以通过 Azure 表存储 使用表。 需要为所要使用的服务创建一个帐户。

创建 Azure 存储帐户

创建第一个存储帐户的最简单方法是使用 Azure 门户。 若要了解更多信息,请参阅 创建存储帐户

也可以使用 Azure PowerShellAzure CLI 创建存储帐户。

如果暂时不想创建存储帐户,也可以使用 Azure 存储模拟器在本地环境中运行和测试代码。 有关详细信息,请参阅使用 Azure 存储模拟器进行开发和测试

创建 PHP 应用程序

创建可访问存储表服务 的 PHP 应用程序的唯一要求是在代码中引用用于 PHP 的 Azure 存储表 SDK 中的类。 可以使用任何开发工具(包括“记事本”)创建应用程序。

本指南涉及使用存储表服务功能,可在 PHP 应用程序中本地调用,或在 Azure Web 角色、辅助角色或网站中运行的代码内调用这些功能。

获取客户端库

  1. 在项目的根目录中创建一个名为 composer.json 的文件并向其添加以下代码: json { "require": { "microsoft/azure-storage-table": "*" } }
  2. composer.phar 下载到根目录中。
  3. 打开命令提示符并在项目根目录中执行以下命令: php composer.phar install 或者转到 GitHub 上的 Azure 存储表 PHP 客户端库,然后克隆源代码。

添加所需引用

若要使用存储表服务,必须:

  • 使用 require_once语句引用 autoloader 文件,并
  • 引用所用的任何类。

以下示例演示如何添加 autoloader 文件和引用 TableRestProxy 类。

require_once 'vendor/autoload.php';
use MicrosoftAzure\Storage\Table\TableRestProxy;

在下面的示例中,始终显示 require_once 语句,但只会引用执行该示例所需的类。

添加存储表服务连接

若要实例化存储表服务客户端,必须首先具有有效的连接字符串。 存储表服务连接字符串的格式如下:

$connectionString = "DefaultEndpointsProtocol=[http|https];AccountName=[yourAccount];AccountKey=[yourKey];TableEndpoint=https://<yourstoragename>.table.core.chinacloudapi.cn/"

添加存储模拟器连接

若要访问模拟器存储,请执行以下操作:

UseDevelopmentStorage=true

若要创建 Azure 表服务客户端,需要使用 TableRestProxy 类。 方法:

  • 将连接字符串直接传递给此类或
  • 使用 CloudConfigurationManager (CCM) 检查多个外部源以获取连接字符串:
    • 默认情况下,它附带了对一个外部源的支持 - 环境变量。
    • 可通过扩展 ConnectionStringSource 类来添加新源。

在此处列出的示例中,将直接传递连接字符串。

require_once 'vendor/autoload.php';

use MicrosoftAzure\Storage\Table\TableRestProxy;

$tableClient = TableRestProxy::createTableService($connectionString);

创建表

利用 TableRestProxy 对象,可以使用 createTable 方法创建表。 创建表时,可以设置表服务超时。 (有关表服务超时的详细信息,请参阅为表服务操作设置超时。)

require_once 'vendor\autoload.php';

use MicrosoftAzure\Storage\Table\TableRestProxy;
use MicrosoftAzure\Storage\Common\ServiceException;

// Create Table REST proxy.
$tableClient = TableRestProxy::createTableService($connectionString);

try    {
    // Create table.
    $tableClient->createTable("mytable");
}
catch(ServiceException $e){
    $code = $e->getCode();
    $error_message = $e->getMessage();
    // Handle exception based on error codes and messages.
    // Error codes and messages can be found here:
    // https://docs.microsoft.com/rest/api/storageservices/Table-Service-Error-Codes
}

有关表名称的限制的信息,请参阅 了解表服务数据模型

将实体添加到表

若要将实体添加到表,请创建新的 Entity 对象并将其传递到 TableRestProxy->insertEntity。 请注意,在创建实体时,你必须指定 PartitionKeyRowKey。 这些值是条目的唯一标识符,查询它们比查询其他条目属性快得多。 系统使用 PartitionKey 自动将表的实体分发到多个存储节点上。 具有相同 PartitionKey 的实体存储在同一个节点上。 (对存储在同一节点上的多个实体执行操作要将比对存储在不同节点上的实体执行的操作的效果更佳。)RowKey 是实体在分区中的唯一 ID。

require_once 'vendor/autoload.php';

use MicrosoftAzure\Storage\Table\TableRestProxy;
use MicrosoftAzure\Storage\Common\ServiceException;
use MicrosoftAzure\Storage\Table\Models\Entity;
use MicrosoftAzure\Storage\Table\Models\EdmType;

// Create table REST proxy.
$tableClient = TableRestProxy::createTableService($connectionString);

$entity = new Entity();
$entity->setPartitionKey("tasksSeattle");
$entity->setRowKey("1");
$entity->addProperty("Description", null, "Take out the trash.");
$entity->addProperty("DueDate",
                        EdmType::DATETIME,
                        new DateTime("2012-11-05T08:15:00-08:00"));
$entity->addProperty("Location", EdmType::STRING, "Home");

try{
    $tableClient->insertEntity("mytable", $entity);
}
catch(ServiceException $e){
    // Handle exception based on error codes and messages.
    // Error codes and messages are here:
    // https://docs.microsoft.com/rest/api/storageservices/Table-Service-Error-Codes
    $code = $e->getCode();
    $error_message = $e->getMessage();
}

有关表属性和类型的信息,请参阅 了解表服务数据模型

TableRestProxy 类提供了用于插入实体的两个替代方法:insertOrMergeEntityinsertOrReplaceEntity。 要使用这些方法,请创建一个新的 Entity ,并将其作为参数传递到上述任一方法。 如果条目不存在,则每种方法都会插入条目。 在实体已存在的情况下,如果属性已存在,则 insertOrMergeEntity 更新属性值;如果属性不存在,则该方法添加新属性,而 insertOrReplaceEntity 会完全替换现有实体。 下面的示例演示如何使用 insertOrMergeEntity。 如果实体具有 PartitionKey“tasksSeattle”并且 RowKey“1”不存在,则将插入该实体。 但是,如果之前已插入该实体(如上面的示例所示),则将更新 DueDate 属性并添加 Status 属性。 系统还将更新 DescriptionLocation 属性,但使用的值实际上会使其保持不变。 如果并非如示例所示添加后两个属性,而这两个数学已存在于目标条目上,则其现有值保持不变。

require_once 'vendor/autoload.php';

use MicrosoftAzure\Storage\Table\TableRestProxy;
use MicrosoftAzure\Storage\Common\ServiceException;
use MicrosoftAzure\Storage\Table\Models\Entity;
use MicrosoftAzure\Storage\Table\Models\EdmType;

// Create table REST proxy.
$tableClient = TableRestProxy::createTableService($connectionString);

//Create new entity.
$entity = new Entity();

// PartitionKey and RowKey are required.
$entity->setPartitionKey("tasksSeattle");
$entity->setRowKey("1");

// If entity exists, existing properties are updated with new values and
// new properties are added. Missing properties are unchanged.
$entity->addProperty("Description", null, "Take out the trash.");
$entity->addProperty("DueDate", EdmType::DATETIME, new DateTime()); // Modified the DueDate field.
$entity->addProperty("Location", EdmType::STRING, "Home");
$entity->addProperty("Status", EdmType::STRING, "Complete"); // Added Status field.

try    {
    // Calling insertOrReplaceEntity, instead of insertOrMergeEntity as shown,
    // would simply replace the entity with PartitionKey "tasksSeattle" and RowKey "1".
    $tableClient->insertOrMergeEntity("mytable", $entity);
}
catch(ServiceException $e){
    // Handle exception based on error codes and messages.
    // Error codes and messages are here:
    // https://docs.microsoft.com/rest/api/storageservices/Table-Service-Error-Codes
    $code = $e->getCode();
    $error_message = $e->getMessage();
    echo $code.": ".$error_message."<br />";
}

检索单个实体

利用 TableRestProxy->getEntity 方法,可以通过查询实体的 PartitionKeyRowKey 来检索它。 在以下示例中,分区键 tasksSeattle 和行键 1 传递给 getEntity 方法。

require_once 'vendor/autoload.php';

use MicrosoftAzure\Storage\Table\TableRestProxy;
use MicrosoftAzure\Storage\Common\ServiceException;

// Create table REST proxy.
$tableClient = TableRestProxy::createTableService($connectionString);

try    {
    $result = $tableClient->getEntity("mytable", "tasksSeattle", 1);
}
catch(ServiceException $e){
    // Handle exception based on error codes and messages.
    // Error codes and messages are here:
    // https://docs.microsoft.com/rest/api/storageservices/Table-Service-Error-Codes
    $code = $e->getCode();
    $error_message = $e->getMessage();
    echo $code.": ".$error_message."<br />";
}

$entity = $result->getEntity();

echo $entity->getPartitionKey().":".$entity->getRowKey();

检索分区中的所有条目

使用筛选器来构造实体查询(有关详细信息,请参阅查询表和实体)。 若要检索分区中的所有实体,请使用筛选器“PartitionKey eq partition_name”。 下面的示例演示了如何通过将筛选器传递到 queryEntities 方法来检索 tasksSeattle 分区中的所有实体。

require_once 'vendor/autoload.php';

use MicrosoftAzure\Storage\Table\TableRestProxy;
use MicrosoftAzure\Storage\Common\ServiceException;

// Create table REST proxy.
$tableClient = TableRestProxy::createTableService($connectionString);

$filter = "PartitionKey eq 'tasksSeattle'";

try    {
    $result = $tableClient->queryEntities("mytable", $filter);
}
catch(ServiceException $e){
    // Handle exception based on error codes and messages.
    // Error codes and messages are here:
    // https://docs.microsoft.com/rest/api/storageservices/Table-Service-Error-Codes
    $code = $e->getCode();
    $error_message = $e->getMessage();
    echo $code.": ".$error_message."<br />";
}

$entities = $result->getEntities();

foreach($entities as $entity){
    echo $entity->getPartitionKey().":".$entity->getRowKey()."<br />";
}

检索分区中的一部分实体

可以使用上一示例中使用的同一模式来检索分区中的部分实体。 检索的实体子集将由所使用的筛选器确定(有关详细信息,请参阅查询表和实体)。下面的示例演示如何使用筛选器检索具有特定的 Location 和早于 DueDate 的所有实体。

require_once 'vendor/autoload.php';

use MicrosoftAzure\Storage\Table\TableRestProxy;
use MicrosoftAzure\Storage\Common\ServiceException;

// Create table REST proxy.
$tableClient = TableRestProxy::createTableService($connectionString);

$filter = "Location eq 'Office' and DueDate lt '2012-11-5'";

try    {
    $result = $tableClient->queryEntities("mytable", $filter);
}
catch(ServiceException $e){
    // Handle exception based on error codes and messages.
    // Error codes and messages are here:
    // https://docs.microsoft.com/rest/api/storageservices/Table-Service-Error-Codes
    $code = $e->getCode();
    $error_message = $e->getMessage();
    echo $code.": ".$error_message."<br />";
}

$entities = $result->getEntities();

foreach($entities as $entity){
    echo $entity->getPartitionKey().":".$entity->getRowKey()."<br />";
}

检索一部分实体属性

查询可检索一部分实体属性。 此方法称为“投影” ,可减少带宽并提高查询性能,尤其适用于大型实体。 若要指定要检索的属性,请将该属性的名称传递到 Query->addSelectField 方法。 可以多次调用此方法来添加更多属性。 执行 TableRestProxy->queryEntities 后,返回的实体将仅具有选定的属性。 (若要返回一部分表实体,请使用上述查询中所示的筛选器。)

require_once 'vendor/autoload.php';

use MicrosoftAzure\Storage\Table\TableRestProxy;
use MicrosoftAzure\Storage\Common\ServiceException;
use MicrosoftAzure\Storage\Table\Models\QueryEntitiesOptions;

// Create table REST proxy.
$tableClient = TableRestProxy::createTableService($connectionString);

$options = new QueryEntitiesOptions();
$options->addSelectField("Description");

try    {
    $result = $tableClient->queryEntities("mytable", $options);
}
catch(ServiceException $e){
    // Handle exception based on error codes and messages.
    // Error codes and messages are here:
    // https://docs.microsoft.com/rest/api/storageservices/Table-Service-Error-Codes
    $code = $e->getCode();
    $error_message = $e->getMessage();
    echo $code.": ".$error_message."<br />";
}

// All entities in the table are returned, regardless of whether
// they have the Description field.
// To limit the results returned, use a filter.
$entities = $result->getEntities();

foreach($entities as $entity){
    $description = $entity->getProperty("Description")->getValue();
    echo $description."<br />";
}

更新条目

可通过对现有实体使用 Entity->setPropertyEntity->addProperty 方法并调用 TableRestProxy->updateEntity 来更新该实体。 下面的示例检索一个条目、修改一个属性、删除另一个属性并添加一个新属性。 请注意,通过将属性的值设为 null 可删除该属性。

require_once 'vendor/autoload.php';

use MicrosoftAzure\Storage\Table\TableRestProxy;
use MicrosoftAzure\Storage\Common\ServiceException;
use MicrosoftAzure\Storage\Table\Models\Entity;
use MicrosoftAzure\Storage\Table\Models\EdmType;

// Create table REST proxy.
$tableClient = TableRestProxy::createTableService($connectionString);

$result = $tableClient->getEntity("mytable", "tasksSeattle", 1);

$entity = $result->getEntity();
$entity->setPropertyValue("DueDate", new DateTime()); //Modified DueDate.
$entity->setPropertyValue("Location", null); //Removed Location.
$entity->addProperty("Status", EdmType::STRING, "In progress"); //Added Status.

try    {
    $tableClient->updateEntity("mytable", $entity);
}
catch(ServiceException $e){
    // Handle exception based on error codes and messages.
    // Error codes and messages are here:
    // https://docs.microsoft.com/rest/api/storageservices/Table-Service-Error-Codes
    $code = $e->getCode();
    $error_message = $e->getMessage();
    echo $code.": ".$error_message."<br />";
}

删除条目

若要删除实体,请将表名称以及实体的 PartitionKeyRowKey 传递到 TableRestProxy->deleteEntity 方法。

require_once 'vendor/autoload.php';

use MicrosoftAzure\Storage\Table\TableRestProxy;
use MicrosoftAzure\Storage\Common\ServiceException;

// Create table REST proxy.
$tableClient = TableRestProxy::createTableService($connectionString);

try    {
    // Delete entity.
    $tableClient->deleteEntity("mytable", "tasksSeattle", "2");
}
catch(ServiceException $e){
    // Handle exception based on error codes and messages.
    // Error codes and messages are here:
    // https://docs.microsoft.com/rest/api/storageservices/Table-Service-Error-Codes
    $code = $e->getCode();
    $error_message = $e->getMessage();
    echo $code.": ".$error_message."<br />";
}

为了进行并发检查,可以使用 DeleteEntityOptions->setEtag 方法并将 DeleteEntityOptions 对象作为第四个参数传递到 deleteEntity,来为要删除的实体设置 Etag。

批量进行表操作

利用 TableRestProxy->batch 方法,可以通过一个请求执行多个操作。 此处的模式涉及将操作添加到 BatchRequest对象,然后将 BatchRequest 对象传递到 TableRestProxy->batch 方法。 要将操作添加到 BatchRequest 对象,可以多次调用以下任一方法:

  • addInsertEntity(添加 insertEntity 操作)
  • addUpdateEntity(添加 updateEntity 操作)
  • addMergeEntity(添加 mergeEntity 操作)
  • addInsertOrReplaceEntity(添加 insertOrReplaceEntity 操作)
  • addInsertOrMergeEntity(添加 insertOrMergeEntity 操作)
  • addDeleteEntity(添加 deleteEntity 操作)

下面的示例演示了如何通过单个请求执行 insertEntitydeleteEntity 操作。

Note

Azure Cosmos DB 尚不支持对表进行批处理操作。

require_once 'vendor/autoload.php';

use MicrosoftAzure\Storage\Table\TableRestProxy; use MicrosoftAzure\Storage\Common\ServiceException; use MicrosoftAzure\Storage\Table\Models\Entity; use MicrosoftAzure\Storage\Table\Models\EdmType; use MicrosoftAzure\Storage\Table\Models\BatchOperations;

// Configure a connection string for Storage Table service. $connectionString = "DefaultEndpointsProtocol=[http|https];AccountName=[yourAccount];AccountKey=[yourKey];TableEndpoint=https://.table.core.chinacloudapi.cn/"

// Create table REST proxy. $tableClient = TableRestProxy::createTableService($connectionString);

// Create list of batch operation. $operations = new BatchOperations();

$entity1 = new Entity(); $entity1->setPartitionKey("tasksSeattle"); $entity1->setRowKey("2"); $entity1->addProperty("Description", null, "Clean roof gutters."); $entity1->addProperty("DueDate", EdmType::DATETIME, new DateTime("2012-11-05T08:15:00-08:00")); $entity1->addProperty("Location", EdmType::STRING, "Home");

// Add operation to list of batch operations. $operations->addInsertEntity("mytable", $entity1);

// Add operation to list of batch operations. $operations->addDeleteEntity("mytable", "tasksSeattle", "1");

try { $tableClient->batch($operations); } catch(ServiceException $e){ // Handle exception based on error codes and messages. // Error codes and messages are here: // https://docs.microsoft.com/rest/api/storageservices/Table-Service-Error-Codes $code = $e->getCode(); $error_message = $e->getMessage(); echo $code.": ".$error_message."
"; }


有关对表操作进行批处理的详细信息,请参阅 [执行实体组事务][entity-group-transactions]。

## <a name="delete-a-table"></a>删除表
最后,若要删除表,请将表名传递到 **TableRestProxy->deleteTable** 方法。

```php
require_once 'vendor/autoload.php';

use MicrosoftAzure\Storage\Table\TableRestProxy;
use MicrosoftAzure\Storage\Common\ServiceException;

// Create table REST proxy.
$tableClient = TableRestProxy::createTableService($connectionString);

try    {
    // Delete table.
    $tableClient->deleteTable("mytable");
}
catch(ServiceException $e){
    // Handle exception based on error codes and messages.
    // Error codes and messages are here:
    // https://docs.microsoft.com/rest/api/storageservices/Table-Service-Error-Codes
    $code = $e->getCode();
    $error_message = $e->getMessage();
    echo $code.": ".$error_message."<br />";
}

后续步骤

既已了解 Azure 表服务和 Azure Cosmos DB 的基础知识,可单击以下链接了解更多信息。