2009-10-30 2 views
1

У меня есть таблица DB, в которой каждая строка имеет случайно сгенерированный первичный ключ, сообщение и пользователь. Каждый пользователь имеет около 10-100 сообщений, но есть 10k-50k пользователей.Удаление строк из конкурирующей таблицы

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

Сейчас я сделать это эффективно:

delete from table where user='mk' 

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

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

У меня нет прямого доступа к БД. Я пытаюсь угадать проблему, основанную на некоторой обратной связи. Причина, по которой я сосредотачиваюсь на этом сценарии, заключается в том, что запрос на удаление показывает много времени ожидания (опять же - насколько мне известно) плюс его добавленный бит функциональности.

Может кто-нибудь предложить какие-либо советы?

было бы лучше:

select key from table where user='mk' 

Затем удалить отдельные строки оттуда? Я думаю, что это может привести к менее жестокой блокировке.

+0

Это действительно довольно сложно agnose database performance problmes, когда мы «не имеем прямого доступа к БД». мы можем провести весь день.Было бы полезно, если бы вы могли выполнить некоторые тесты. В частности, вам действительно нужно запустить трассировку 10046, чтобы увидеть, где ожидает инструкция DELETE. Итак, можете ли вы получить прямой доступ к базе данных? – APC

ответ

3

Нет, всегда лучше выполнять один оператор SQL в наборе строк, чем ряд «строк за строкой» (или то, что Tom Kyte называет «медленными медленными») операциями. Когда вы говорите, что видите «много споров», что вы видите точно? Очевидный вопрос: индексируется столбец USER?

(Конечно, имя столбца не может быть действительно USER в базе данных Oracle, так как это зарезервированное слово!)

EDIT: Вы сказали, что колонка USER не индексируется. Это означает, что каждое удаление будет включать полное сканирование таблицы до 50K * 100 = 5 миллионов строк (или в лучшем случае 10K * 10 = 100 000 строк), чтобы удалить всего 10-100 строк. Добавление индекса в USER может решить ваши проблемы.

+0

-1 Ряд строк за строкой может быть медленнее при работе на автономной машине. Но они будут вызывать меньше конфликтов, и в системе, которая находится под устойчивым использованием, обычно ускоряется в целом (по моему опыту) – Andomar

+0

Ли удаление одной команды приводит к тому, что больше конфликтов зависит от базы данных и шаблона доступа к этим записям. Почему-то я сомневаюсь, что пользователи читают свои сообщения так часто, что они вызывают существенные конфликты с удалением. –

+0

Кроме того, если проблема связана с диском, использование большего количества транзакций приведет к конфликту _more_ disk. –

0

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

4

Если вы делаете это каждый день для каждого пользователя, почему бы просто не удалить каждую запись из таблицы в одном выражении? Или даже

truncate table whatever reuse storage 
/

редактировать

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

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

+0

Почему это проголосовали? В вопросе говорится: «Я хочу выбросить старые сообщения для каждого пользователя», но это отбрасывает все сообщения. – Andomar

+0

@Andomar - имеет смысл для меня, если вы принимаете вопрос по номиналу - кажется, что требование состоит в том, чтобы иметь ценность для каждого дня для каждого пользователя, так почему бы просто не очистить таблицу и написать все новые сообщения за день? – dpbradley

+0

@dpbradley: Хорошо ... Я вижу, вы можете прочитать вопрос таким образом. Если это то, что означает OP, это определенно оптимальный ответ. :) – Andomar

-4

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

  • сделка будет меньше
  • замок будет ограничена лишь несколькими строки в то время

Блокировка в партиях должна быть большим шагом вперед.

+1

-1 Да, партия ВСЕХ 10-100 из них была бы лучшей! –

+1

В Oracle, имеющем небольшие транзакции, не является преимуществом: объем выполненной работы будет меньше в одном БОЛЬШОМ удалении, чем в MANY небольших операциях delete. Кроме того, если есть только один модифицирующий сеанс (в данном случае пакетное задание), блокировка не имеет значения: массовое обновление не будет блокировать чтение в любом случае. Все рассмотренные массивные обновления/удаления часто более эффективны в Oracle. –

+0

@Vincent: Согласен. Я прочитал запрос, чтобы иметь в виду 500 000 обновлений в конфликте с другими запросами, такими как установка «прочитанных» флагов. – Andomar

0

Я думаю, вам нужно определить свои требования немного яснее ...

Например. Если вы знаете всех пользователей, для которых вы хотите писать сообщения, вставьте идентификаторы в временную таблицу, проиндексируйте их по идентификатору и пакетному удалению. Затем потоки, которые вы стреляете, делают две вещи. Введите идентификатор пользователя в временную таблицу, напишите сообщение в другую таблицу темп. Затем, когда потоки завершили выполнение, основной поток должен

DELETE * FROM сообщения INNER JOIN TEMP_MEMBERS ON ID = TEMP_ID

INSERT INTO СООБЩЕНИЙ SELECT * FROM TEMP_messges

им не знакомы с синтаксисом Oracle, но таким образом я бы подошел к нему, если все сообщения пользователей выполняются быстро.

Надеется, что это помогает

+0

Обратите внимание, что синтаксис INNER JOIN на DELETE является нестандартным и не работает с Oracle. Вам нужно будет использовать инструкцию DELETE ... WHERE. – DCookie

+0

Нет, он работает с Oracle, если существует уникальный или первичный ключ в столбце TEMP_MEMBERS.TEMP_ID –

0

РАЗГОВОРУ НА ВАШЕГО DBA

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

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

Действительно, поговорите с вашим администратором баз данных. Вероятно, ему понравится смотреть на что-то забавное, а не на планирование последнего развертывания ЦП.

-2

Это может ускорить процесс:

Создать таблицу поиска:

create table rowid_table (row_id ROWID ,user VARCHAR2(100)); 
create index rowid_table_ix1 on rowid_table (user); 

Выполнить ночные работы:

truncate table rowid_table; 
insert /*+ append */ into rowid_table 
select ROWID row_id , user 
from table; 
dbms_stats.gather_table_stats('SCHEMAOWNER','ROWID_TABLE'); 

Тогда при удалении записей:

delete from table 
where ROWID IN (select row_id 
       from rowid_table 
       where user = 'mk'); 
+0

Вы понимаете, что таблица поиска выполняет работу индекса? – Rob

+0

Индекс таблицы поиска выполняет работу индекса. Если у плаката действительно есть конкуренция по таблице и, возможно, индексы на этой таблице (возможно, с OLTP-транзакциями), она переводит io на другой объект. –