Поделиться через


Основы хранилища Windows Azure. Таблицы.(ru-RU)

Таблицы

http://hpcru.files.wordpress.com/2012/03/clip_image001_thumb.png

Сервис таблиц в Windows Azure не является реляционным.

Нельзя-

1) Создавать внешние ключи между таблицами.

2) Выполнять операцию объединения на стороне сервера

3) Создавать произвольные индексы

4) Выполнять такие функции, как, например, Count(), на стороне сервера

Применение сервиса таблиц актуально, если вам не требуется реляционное хранилище или если вам не нужны операции объединения (joins) на стороне сервера, а также в тех случаях, когда набор данных не очень велик и операции объединения можно обрабатывать на стороне клиента с помощью LINQ. Кроме этого, стоит оценить выгоды использования сервиса таблиц хранилища Windows Azure в том случае, если данных у вас больше, чем максимальный объем, поддерживаемый SQL Azure (который в настоящее время составляет 150 Гб). Заметьте

Таблицы Windows Azure хранят данные в виде коллекций сущностей. Сущности аналогичны записям. Сущность имеет первичный ключ и набор свойств. Свойства являются парами ключ-значения, что аналогично столбцам.

Для доступа к сервису таблиц вы можете использовать REST API, совместимый с WCF Data Services (ранее ADO.NET Data Services Framework). Для чтения и записи данных в сервис таблиц в упражнении используется WCF Data Services Client Library (ранее .NET Client Library).

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

  • Локальный эмулятор не поддерживает операции над сущностями Insert-Or-Replace и Insert-Or-Merge, известными как upsert-функциональность. 

  • Локальный эмулятор не поддерживает создание проекций сущностей. 

  • Свойства Date в локальном эмуляторе сервиса хранилища таблиц поддерживает диапазоны, поддерживаемые SQL Server 2005 (например, необходимо, чтобы они имели дату, более позднюю нежели 1 января, 1753. Все даты до этого момента будут сведены к 1 января, 1753 года. Точность дат ограничивается точностью SQL Server 2005, что значит, что даты имеют точность 1/300 секунды. 

  • Локальный эмулятор поддерживает значения partition key и row key, меньшие 900 байт. Полный размер имени аккаунта, имени таблицы и имен ключевых свойств не должен превышать 900 байт. 

  • Общий размер записи в таблице в локальном эмуляторе ограничен до 1 Мб. 

  • Локальный эмулятор не проверяет, меньше ли 4 Мб размер пакета (batch) в транзакции группы сущностей. В Windows Azure 
    пакеты ограничены до 4 Мб в , поэтому вы должны быть уверены, что пакет не превышает это ограничение, перед тем, как переходить к облачным сервисам хранилища в Windows Azure. 

  • В локальном эмуляторе запрос к свойству, которого не существует в таблице, возвратит ошибку. Такой же запрос не возвратит ошибку в облаке. 

  • В локальном эмуляторе свойства типа данных Edm.Guid и Edm.Binary в строках фильтров поддерживают только сравнения типа Equal (eq) и NotEqual (ne).

  • Локальный эмулятор хранилища не поддерживает Microsoft.WindowsAzure.StorageClient.CloudTableClient.SetServiceProperties(Microsoft.WindowsAzure.StorageClient.Protocol.ServiceProperties).

Запись в таблицах состоит из следующих компонентов и концепций:

  • Свойство (столбец) – значение сущности. Имена свойств регистрочувствительны.
  • PartitionKey – первое обязательное свойство каждой таблицы, используемое системой для автоматической балансировки нагрузки и распределения сущностей таблицы между серверами.
  • RowKey – второе обязательно свойство для акждой таблицы, являющееся уникальным ID сущности внутри партиции, в которой оно расположено, и являющееся вторым компонентом в комбинации с RowKey, уникально идентифицирующей сущность в таблице.
  • Timestamp – каждая сущность имеет версию, управляемую системой, необходимую для оптимистического параллелизма.

Пример: POCO, наследуемый от TableServiceEntity

public class MyClass : TableServiceEntity
        {

            public MyClass()
            {

                base.PartitionKey = "Clients";

                base.RowKey = Guid.NewGuid().ToString();

            }

            public MyClass(string PartitionKey, string RowKey)
            {

                base.PartitionKey = PartitionKey;

                base.RowKey = RowKey;

            }

            public string FName { get; set; }

            public string LName { get; set; }

        }

Пример: сокращенная запись

public MyClass():base("Clients", Guid.NewGuid().ToString())
{
}

public MyClass(string PartitionKey, string RowKey):base(PartitionKey,RowKey)
{
}

Класс TableServiceEntity является частью библиотеки Microsoft.WindowsAzure.StorageClient. Он определяет системные свойства PartititionKey, RowKey и TimeStamp, необходимые для каждой сущности в таблице Windows Azure. данном случае мы определяем для свойства PartitionKey фиксированное значение. В реальной ситуации лучше выбирать значение, обеспечивающее балансировку нагрузки между узлами хранилища, динамическое вычисляемое значение, например, день добавления сущности и так далее.

  • Порядок сортировки – есть единственный индекс в таблицах, когда таблицы сортируются по сначала PartitionKey, после чего по RowKey, что означает, что записи, указывающие эти ключи, будут более эффективны, и результаты будут сортироваться сначала по PartitionKey и потом по RowKey.
  • Типы – PartitionKey и RowKey должны быть типа string, остальные свойства: Binary, Bool, DateTime, Double, GUID, Int, Int64, String.
  • Нет фиксированной схемы – в таблицах Windows Azure нет схем, поэтому все свойства хранятся в парах ключ-типизированное значение, что означает, что две сущности в одной таблице могут иметь разные свойства. В таблице может быть даже два свойства с одинаковым имене, но разными типами для значения свойства. В пределах одной сущности имена свойств должны быть уникальными.

Ссылка на таблицу выглядит стандартно для именования сущностей в сервисах хранилища Windows Azure:

http://<account>.table.core.windows.net/<TableName>

Рекомендация: таблицы данных должны создаваться только один раз. Обычно этот процесс выполняется на стадии подготовки и редко – в коде приложения. Application_Start в классе Global является рекомендуемым местом для помещения логики инициализации.

        void Application_Start(object sender, EventArgs e)
        {
            CloudStorageAccount.SetConfigurationSettingPublisher((configName, configSettingPublisher) =>
                                                                     {
                                                                         var connectionString =
                                                                             RoleEnvironment.
                                                                                 GetConfigurationSettingValue(configName);
                                                                         configSettingPublisher(connectionString);
                                                                     });
            //получение данных из настроек аккаунта хранилища, в данном случае это строка подключения DataStoragevar account = CloudStorageAccount.FromConfigurationSetting("DataStorage");//создание таблиц согласно контексту данных
            CloudTableClient.CreateTablesFromModel(typeof(MyClassDataContext),
                                    account.TableEndpoint.AbsoluteUri, account.Credentials);
        }

Для удобства и аккуратности программирования необходимо выполнять все операции над определенными таблицами из соответствующих классов контекстов, которые наследуются от класса DataServiceContext.

using System.Linq;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.StorageClient;

namespace WebRole
{
    public class MyClassDataContext : TableServiceContext
    {
        public MyClassDataContext(string baseAddress, StorageCredentials credentials)
            : base(baseAddress, credentials)
        {
        }

        public IQueryable<MyClass> units
        {
            get
            {
                return this.CreateQuery<MyClass>("units");
            }
        }
    }

}

Для использования и манипуляций над содержимым таблицы необходимо создать объект типа CloudTableClient, предоставляющий следующую функциональность:

CreateTable – создание таблицы с определенным именем. В случае наличия таблицы с таким именем будет выброшено исключение StorageClientException. Наиболее предпочитаемым способом создания таблиц является их создание с помощью класса контекста.

CloudTableClient.CreateTablesFromModel(typeof(MyClassDataContext),
                                    account.TableEndpoint.AbsoluteUri, account.Credentials);
        }

CreateTableIfNotExist – создание таблицы с определенным именем только в том случае если ее не существует.

DoesTableExist – проверка на существование таблицы с определенным именем.

DeleteTable – удаление таблицы и ее содержимого из хранилища. если этой таблицы не существует, будет выброшено исключение StorageClientException.

DeleteTableIfExist – удаление таблицы и содержимого из хранилища только в том случае если она существует.

        public static void DeleteTable()
        {

            var account = CloudStorageAccount.FromConfigurationSetting("DataStorage");
            account.CreateCloudTableClient().DeleteTableIfExist("units");

        }

ListTables – получение списка всех таблиц. Возможно указание префикса для фильтрации имен таблиц.

        public static void GetListTables()
        {
            var account = CloudStorageAccount.FromConfigurationSetting("DataStorage");
            IEnumerable<string> tables = account.CreateCloudTableClient().ListTables("mytableprefix");
            foreach (var table in tables)
            {
                System.Diagnostics.Trace.WriteLine(table);
            }
            
        }

AddObject – добавление сущности в таблицу.

public static void AddEntityToTable(){ var account = CloudStorageAccount.FromConfigurationSetting("DataStorage");

var tableClient = account.CreateCloudTableClient();

TableServiceContext ctx = tableClient.GetDataServiceContext();

MyClass unit = new MyClass("Alex", "Belotserkovskiy");

unit.FName = “FName”;unit.LName = “LName”;
ctx.AddObject("mytable", customer1);

serviceContext.SaveChangesWithRetries();}

В том случае, если у вас происходит создание множества сущностей, дешевле (с позиции количества транзакций) и быстрее будет выполнить пакет запросов. Для этого всего лишь надо передать в SaveChangesWithRetries аргумент. Пакеты запросов характеризуются:

  1. В пакете можно совершать запросы update, delete и insert.
  2. Один пакет может содержать до 100 сущностей.
  3. Все сущности в одном пакете должны иметь один partition key.
ctx.SaveChangesWithRetries(SaveChangesOptions.Batch);

Получение объектов может быть реализовано с помощью LINQ-утверждения. Для возвращения всех записей в пределах одной партиции и имеющих определенное значение поля, можно воспользоваться следующим LINQ-утверждением.

CloudTableQuery<MyClass> qry =
    (from e in ctx.CreateQuery<MyClass>("mytable")
     where e.PartitionKey == "Belotserkovskiy" && e.FName == “Alex”
     select e).AsTableServiceQuery<MyClass>();

foreach (MyClass unit in qry)
{
    Console.WriteLine("{0}, {1}",  unit.PartitionKey, unit.FName);
}

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

MyClass unit = (from e in ctx.CreateQuery<MyClass>("mytable") where e.PartitionKey == "Belotserkovskiy" && e.FName == “Alex” select e).AsTableServiceQuery<MyClass>().FirstOrDefault();unit.FName= "Mike";

ctx.UpdateObject(unit);
ctx.SaveChangesWithRetries(); ctx.DeleteObject(unit);
ctx.SaveChangesWithRetries();