Горе Amazon SimpleDB: Реализация встречных атрибутов

Здесь Вы идете:

:nmap <F1> :w<cr>:!%<cr>

сохраняют & выполненный (необходимо быть в n режиме, хотя - просто добавляют esc и поскольку я режим)

9
задан Steffen Opel 6 January 2013 в 13:29
поделиться

4 ответа

Существующий API SimpleDB не может быть распределенным счетчиком. Но это, безусловно, можно сделать.

Работая строго в SimpleDB, есть два способа заставить его работать. Простой метод, для очистки которого требуется что-то вроде работы cron. Или гораздо более сложная техника, которая очищает по ходу дела.

Простой способ

Легкий способ - делать разные предметы для каждого «удара». С единственным атрибутом, который является ключевым. Прокачайте домен (ы) счетчиками быстро и легко. Когда вам нужно получить счетчик (предположительно, гораздо реже), вы должны выполнить запрос

SELECT count(*) FROM domain WHERE key='myKey'

. Конечно, это приведет к неограниченному росту вашего домена (ов), и запросы со временем будут выполняться все дольше и дольше. Решение - это итоговая запись, в которой вы сводите все подсчеты, собранные на данный момент для каждого ключа. Это' s просто элемент с атрибутами для ключа {summary = 'myKey'} и отметкой времени «Последнее обновление» с детализацией до миллисекунды. Это также требует, чтобы вы добавляли атрибут «отметка времени» к вашим «хитам». Сводные записи не обязательно должны находиться в одном домене. Фактически, в зависимости от ваших настроек, их лучше всего хранить в отдельном домене. В любом случае вы можете использовать ключ в качестве itemName и использовать GetAttributes вместо SELECT.

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

SELECT count(*) FROM domain WHERE key='myKey' AND timestamp > '...'

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

Это решение работает в условиях одновременных обновлений, потому что даже если одновременно записывается много итоговых записей, все они верны, и какой бы из них ни был, все равно будет правильным, потому что счетчик и ' «Последнее обновление» будет согласовано друг с другом.

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

Это хорошо работает с кешированием. Если ваш кеш выходит из строя, у вас есть авторитетная резервная копия.

Придет время, когда кто-то захочет вернуться и отредактировать / удалить / добавить запись со старым значением «Timestamp». Вам нужно будет обновить свою сводную запись (для этого домена) в это время, иначе ваши счетчики будут отключены, пока вы не пересчитаете эту сводку.

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

Жесткий путь

Другой способ - использовать обычный механизм чтения - увеличения - хранения, но при этом записать составное значение, которое включает номер версии вместе с вашим значением. Если номер версии, которую вы используете, на 1 больше, чем номер версии значения, которое вы обновляете.

get (key) возвращает значение атрибута = "Ver015 Count089"

Здесь вы получаете счетчик 89, который был сохранен как версия 15. Когда вы выполняете обновление, вы пишете такое значение:

put (key, value = "Ver016 Count090")

Предыдущее значение не удалено, и вы получаете контрольный журнал обновлений, которые напоминают часы lamport.

Это требует от вас сделать несколько дополнительных вещей.

  1. возможность идентифицировать и разрешать конфликты всякий раз, когда вы выполняете GET
  2. простой номер версии не идет работать с тобой ' я захочу включить временную метку с разрешением не менее миллисекунды и, возможно, также идентификатор процесса.
  3. на практике вы хотите, чтобы ваше значение включало номер текущей версии и номер версии значения, на котором основано ваше обновление для более легкого разрешения конфликтов.
  4. вы не можете вести бесконечный контрольный журнал в одном элементе, поэтому вам нужно будет выдавать удаление для старых значений по ходу дела.

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

Когда я говорю разрешать конфликты во время GET, я означает, что если вы читаете элемент, и значение выглядит так:

      11 --- 12
     /
10 --- 11
     \
       11

Вы должны иметь возможность вычислить, что реальное значение равно 14. Что вы можете сделать, если вы включите для каждого нового значения версию обновляемых значений.

Это не должно быть ракетостроением.

Если вам нужен простой счетчик: это способ перебить . Изготовление простого счетчика - это не ракетостроение. Вот почему SimpleDB может быть не лучшим выбором для создания простых счетчиков.

Это не единственный способ, но большинство из этих вещей необходимо будет сделать, если вы реализуете решение SimpleDB вместо фактической блокировки.

Не поймите меня неправильно, мне действительно нравится этот метод именно потому, что нет блокировки, а ограничение на количество процессов, которые могут использовать этот счетчик одновременно, составляет около 100 (из-за ограничения на количество атрибутов в item) И вы можете выйти за пределы 100 с некоторыми изменениями.

Примечание

Но если бы все эти детали реализации были скрыты от вас, и вам просто нужно было бы вызвать инкремент (ключ), это было бы совсем не сложно. В SimpleDB клиентская библиотека является ключом к упрощению сложных вещей. Но в настоящее время нет общедоступных библиотек, реализующих эту функцию (насколько мне известно).

20
ответ дан 4 December 2019 в 06:19
поделиться

I see you've accepted an answer already, but this might count as a novel approach.

If you're building a web app then you can use Google's Analytics product to track page impressions (if the page to domain-item mapping fits) and then to use the Analytics API to periodically push that data up into the items themselves.

I haven't thought this through in detail so there may be holes. I'd actually be quite interested in your feedback on this approach given your experience in the area.

Thanks Scott

2
ответ дан 4 December 2019 в 06:19
поделиться

Для всех, кто интересуется, как я справился с этим ... (немного специфично для Java)

В итоге я использовал EhCache для каждого экземпляра сервлета. Я использовал UUID в качестве ключа и Java AtomicInteger в качестве значения. Периодически поток выполняет итерацию по кешу и отправляет строки в домен временной статистики simpledb, а также записывает строку с ключом в домен аннулирования (что автоматически завершается ошибкой, если ключ уже существует). Поток также уменьшает счетчик на предыдущее значение, гарантируя, что мы не пропустим попаданий во время обновления. Отдельный поток проверяет домен аннулирования simpledb и сводит статистику во временных доменах (для каждого ключа есть несколько строк, поскольку мы используем экземпляры ec2), перемещая его в фактический домен статистики.

Я провел небольшое нагрузочное тестирование, и, похоже, оно хорошо масштабируется. Локально я смог обработать около 500 обращений в секунду до того, как сломался тестер нагрузки (а не сервлеты - ха-ха), так что если что-то, я думаю, работа на ec2 должна только улучшить производительность.

а также запись строки с ключом в домен аннулирования (который не работает без уведомления, если ключ уже существует). Поток также уменьшает счетчик на предыдущее значение, гарантируя, что мы не пропустим попаданий во время обновления. Отдельный поток проверяет домен аннулирования simpledb и сводит статистику во временных доменах (для каждого ключа есть несколько строк, поскольку мы используем экземпляры ec2), перемещая его в фактический домен статистики.

Я провел небольшое нагрузочное тестирование, и, похоже, оно хорошо масштабируется. Локально я смог обработать около 500 обращений в секунду до того, как сломался тестер нагрузки (а не сервлеты - ха-ха), так что если что-то, я думаю, работа на ec2 должна только улучшить производительность.

а также запись строки с ключом в домен аннулирования (который не работает без уведомления, если ключ уже существует). Поток также уменьшает счетчик на предыдущее значение, гарантируя, что мы не пропустим попаданий во время обновления. Отдельный поток проверяет домен аннулирования simpledb и сводит статистику во временных доменах (для каждого ключа есть несколько строк, поскольку мы используем экземпляры ec2), перемещая его в фактический домен статистики.

Я провел небольшое нагрузочное тестирование, и, похоже, оно хорошо масштабируется. Локально я смог обработать около 500 обращений в секунду до того, как сломался тестер нагрузки (а не сервлеты - ха), так что если что-то, на мой взгляд, работа на ec2 должна только улучшить производительность.

Поток также уменьшает счетчик на предыдущее значение, гарантируя, что мы не пропустим попаданий во время обновления. Отдельный поток проверяет домен аннулирования simpledb и сводит статистику во временных доменах (для каждого ключа есть несколько строк, поскольку мы используем экземпляры ec2), перемещая его в фактический домен статистики.

Я провел небольшое нагрузочное тестирование, и, похоже, оно хорошо масштабируется. Локально я смог обработать около 500 обращений в секунду до того, как сломался тестер нагрузки (а не сервлеты - ха-ха), так что если что-то, я думаю, работа на ec2 должна только улучшить производительность.

Поток также уменьшает счетчик на предыдущее значение, гарантируя, что мы не пропустим попаданий во время обновления. Отдельный поток проверяет домен аннулирования simpledb и сводит статистику во временных доменах (для каждого ключа есть несколько строк, поскольку мы используем экземпляры ec2), перемещая его в фактический домен статистики.

Я провел небольшое нагрузочное тестирование, и, похоже, оно хорошо масштабируется. Локально я смог обработать около 500 обращений в секунду до того, как сломался тестер нагрузки (а не сервлеты - ха), так что если что-то, на мой взгляд, работа на ec2 должна только улучшить производительность.

re с использованием экземпляров ec2), перемещая его в фактический домен статистики.

Я провел небольшое нагрузочное тестирование, и, похоже, оно хорошо масштабируется. Локально я смог обработать около 500 обращений в секунду до того, как сломался тестер нагрузки (а не сервлеты - ха-ха), так что если что-то, я думаю, работа на ec2 должна только улучшить производительность.

re с использованием экземпляров ec2), перемещая его в фактический домен статистики.

Я провел небольшое нагрузочное тестирование, и, похоже, оно хорошо масштабируется. Локально я смог обработать около 500 обращений в секунду до того, как сломался тестер нагрузки (а не сервлеты - ха-ха), так что если что-то, я думаю, работа на ec2 должна только улучшить производительность.

2
ответ дан 4 December 2019 в 06:19
поделиться

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

Теперь, чтобы реализовать счетчик, просто вызовите GetAttributes, увеличьте счетчик, а затем вызовите PutAttributes с правильно установленным ожидаемым значением. Если Amazon отвечает с ошибкой ConditionalCheckFailed , повторите всю операцию.

Обратите внимание, что вы можете иметь только одно ожидаемое значение для каждого вызова PutAttributes. Итак, если вы хотите иметь несколько счетчиков в одной строке, используйте атрибут версии.

псевдокод:

begin
  attributes = SimpleDB.GetAttributes
  initial_version = attributes[:version]
  attributes[:counter1] += 3
  attributes[:counter2] += 7
  attributes[:version] += 1
  SimpleDB.PutAttributes(attributes, :expected => {:version => initial_version})
rescue ConditionalCheckFailed
  retry
end
15
ответ дан 4 December 2019 в 06:19
поделиться
Другие вопросы по тегам:

Похожие вопросы: