|
[GlobalsDb] Консультация по удалению данных из GlobalsDb
|
|||
---|---|---|---|
#18+
Доброго времени суток, уважаемые коллеги. Нужна консультация по удалению данных из GlobalsDb. Операционка Windows 7 x64, база GlobalsDb, доступ через java-интерфейс. Приведу упрощенное описание структуры базы данных. В базе есть два основных глобала: 1. objects - содержит перечень номеров объектов от 1 и далее как минимум до 50 млн. Под каждым объектом лежит набор ключей и значений. Набор ключей - это ограниченное множество строк (порядка 30). Обычно у каждого объекта есть порядка 20 ключей. Каждый конкретный ключ конкретного объекта содержит одно значение. objects =1 ==k1=v1 ==k2=v2 =2 ==k2=v6 ==k3=v4 =3 ==k1=v5 =4 ==k1=v1 ... 2. indices - содержит инвертированный индекс всех ключей и их значений, чтобы по заданному наименованию ключа (по сути характеристика) и конкретному значению получить перечень объектов, соответсвующих данному критерию поиска indices =k1 ==v1 ===1 ===4 ==v5 ===3 =k2 ==v2 ===1 ==v6 ===2 =k3 ==v4 ===2 ... Думаю, что идея понятна. Поиск выполняется относительно быстро, а вот с производительностью удаления есть проблема: если удалить всю базу - все удаляется быстро (порядка нескольких секунд), т.к. удаляются глобалы целиком. А вот если нужно удалить, например, 90% объектов (по идентификаторам), то все начинает сильно тормозить (операция не завершается и за несколько часов). Т.е. я беру, например, массив из 40 млн. идентификаторов объектов и в цикле начинаю бежать по нему: 1. сначала достаю из глобала objects ключи и значения 2. для каждого ключа и значения метаданных иду в глобал indices, выполняю навигацию по ключу, затем значению, затем номеру object и делаю kill этого узла. 3. пункт 2 делаю, пока не удалю все индексы для текущего объекта. 4. удаляю сам текущий объект с помощью kill. 5. перехожу к следующему объекту и опять к пункту 2. Если я правильно понимаю причины подтормаживаний, то дело в дисковой подсистеме, для которой достаточно тяжело дается эта операция постоянного "метания" по диску (обычный, не твердотельный), поскольку в таком режиме данные не могут быть расположены смежно на диске. Инвертированный индекс нужен, т.к. одно из требований - поиск по произвольным ключам объектов. Какие варианты рассматривал: 1. Есть вариант с самописным экспортом базы в другой файл, а затем удалить базу целиком и загрузить самописным экспортом данные в чистую базу. Это может работать в сценарии, когда удаляется бОльшая часть данных (например, 90%). 2. Разбить базу на тома (добавив еще один узел в древовидную иерархию), и тогда появляется возможность удалять данные целыми томами, но это не решит задачу произвольного удаления объекта по его номеру (зато можно удалять целыми диапазонами, если все номера попадают в диапазон тома). Еще один минус в том, что индекс тоже придется делать для отдельного тома. Вопросы: 1. Можно ли как-то ускорить (уменьшить время удаления хотя бы до десятков минут-часа) данную операцию с использованием GlobalsDb? Реорганизовать данные наподобие томов, может что-то еще? 2. Что может дать переход на Cache? UPD: s ^objects(1,"k1")="v1" s ^objects(1,"k2")="v2" s ^objects(2,"k2")="v6" s ^objects(2,"k3")="v4" s ^objects(3,"k1")="v5" s ^objects(4,"k1")="v1" s ^indices("k1","v1",1)="" s ^indices("k1","v1",4)="" s ^indices("k1","v5",3)="" s ^indices("k2","v2",1)="" s ^indices("k2","v6",2)="" s ^indices("k3","v4",2)="" Модератор: Добавил структуру глобалов ... |
|||
:
Нравится:
Не нравится:
|
|||
17.07.2015, 00:25 |
|
[GlobalsDb] Консультация по удалению данных из GlobalsDb
|
|||
---|---|---|---|
#18+
TryCache , ты бы для ясности нормально массивы описал... Да тестовый пример с данными подготовил... Я первый раз вижу такое "описание" структуры массива. ... |
|||
:
Нравится:
Не нравится:
|
|||
17.07.2015, 08:27 |
|
[GlobalsDb] Консультация по удалению данных из GlobalsDb
|
|||
---|---|---|---|
#18+
krvsa TryCache , ты бы для ясности нормально массивы описал... Да тестовый пример с данными подготовил...Up ... |
|||
:
Нравится:
Не нравится:
|
|||
17.07.2015, 08:57 |
|
[GlobalsDb] Консультация по удалению данных из GlobalsDb
|
|||
---|---|---|---|
#18+
servit , спасибо... TryCache , в качестве эксперимента... Попробуй следующее. Создай еще один индекс, такого типа. Т.о. удаление будешь вести по новому индексу... А потом просто прибьешь два узла с ИДишником... Возможно будет быстрее. Код: javascript 1. 2. 3. 4. 5. 6.
Это Код: javascript 1.
будет соответствовать Код: javascript 1.
Т.е. нужное имя придется сделать как строку... Можно конечно и все имя хранить... Просто лишние данные в индексе... ... |
|||
:
Нравится:
Не нравится:
|
|||
17.07.2015, 09:22 |
|
[GlobalsDb] Консультация по удалению данных из GlobalsDb
|
|||
---|---|---|---|
#18+
TryCacheбаза GlobalsDb, доступ через java-интерфейс В нормальном М есть job... Удаление можно было бы делать несколькими джобами, что хорошо ускоряется на мощных компах. Java может такое? ... |
|||
:
Нравится:
Не нравится:
|
|||
17.07.2015, 09:24 |
|
[GlobalsDb] Консультация по удалению данных из GlobalsDb
|
|||
---|---|---|---|
#18+
TryCacheсначала достаю из глобала objects ключи и значения т.е. на это шаге вы знаете значение kx, vx и номер объекта? автордля каждого ключа и значения метаданных иду в глобал indices, выполняю навигацию по ключу, затем значению, затем номеру object и делаю kill этого узла. Правильно ли я понял, что на этом шаге вы запускаете цыклы по "миллионам ваших записей" в глобале ^indices и на каждой итерации проверяете совпадает ли значение текущих узлов с "kx, vx и номер объекта" найденными на предыдущем шаге, вместо простого: Код: sql 1.
Так? ... |
|||
:
Нравится:
Не нравится:
|
|||
17.07.2015, 09:33 |
|
[GlobalsDb] Консультация по удалению данных из GlobalsDb
|
|||
---|---|---|---|
#18+
krvsa servit , спасибо... TryCache , в качестве эксперимента... Попробуй следующее. Создай еще один индекс, такого типа. Т.о. удаление будешь вести по новому индексу... А потом просто прибьешь два узла с ИДишником... Возможно будет быстрее. Код: javascript 1. 2. 3. 4. 5. 6.
Это Код: javascript 1.
будет соответствовать Код: javascript 1.
Т.е. нужное имя придется сделать как строку... Можно конечно и все имя хранить... Просто лишние данные в индексе... Что-то не очень пойму как это поможет удалить инвертированный индекс. Там ведь из инвертированного индекса нужно удалить данные тоже. Насколько я понимаю, порядок удаления из инвертированного индекса в общем случае не будет линейным и придется прыгать по файлу (диску) туда-сюда. Думаю, что удаление родительского узла позволит быстро удалить всех потомков, но это при удалении всех потомков без разбору. Если Вы про это, поясните, пожалуйста. Или Вы предлагаете использовать новый индекс взамен старого? ... |
|||
:
Нравится:
Не нравится:
|
|||
17.07.2015, 09:52 |
|
[GlobalsDb] Консультация по удалению данных из GlobalsDb
|
|||
---|---|---|---|
#18+
TryCache, а на мой вопрос ответите? ... |
|||
:
Нравится:
Не нравится:
|
|||
17.07.2015, 09:57 |
|
[GlobalsDb] Консультация по удалению данных из GlobalsDb
|
|||
---|---|---|---|
#18+
П.С.М.TryCacheсначала достаю из глобала objects ключи и значения т.е. на это шаге вы знаете значение kx, vx и номер объекта? автордля каждого ключа и значения метаданных иду в глобал indices, выполняю навигацию по ключу, затем значению, затем номеру object и делаю kill этого узла. Правильно ли я понял, что на этом шаге вы запускаете цыклы по "миллионам ваших записей" в глобале ^indices и на каждой итерации проверяете совпадает ли значение текущих узлов с "kx, vx и номер объекта" найденными на предыдущем шаге, вместо простого: Код: sql 1.
Так? на первый вопрос да , когда считан объект, то я знаю ключи и значения. На второй вопрос скорее нет , т.к. делается appendSubscript(key), затем appendSubscript(value), затем appendSubscript(objId), затем kill для NodeReference. Думаю, это соответствует kill ^indices(kx,vx,"номер объекта"), правда не прямо, но с глобалс по другому и не получится. Кстати, а насколько быстро работает kill ^indices(kx,vx,"номер объекта")? при таких данных там таких записей более миллиарда и они удаляются в общем случае в произвольном порядке. Т.е. нужно удалять порядка 300000 записей в секунду в течение часа. Это возможно физически? ... |
|||
:
Нравится:
Не нравится:
|
|||
17.07.2015, 09:59 |
|
[GlobalsDb] Консультация по удалению данных из GlobalsDb
|
|||
---|---|---|---|
#18+
TryCacheЧто-то не очень пойму как это поможет удалить инвертированный индекс. В моем предложении удаление делается не по данным, а по новому индексу. Ускорит это или нет - тебе нужно попробовать. У меня ведь нет твоих данных... ... |
|||
:
Нравится:
Не нравится:
|
|||
17.07.2015, 10:02 |
|
[GlobalsDb] Консультация по удалению данных из GlobalsDb
|
|||
---|---|---|---|
#18+
TryCacheКстати, а насколько быстро работает kill ^indices(kx,vx,"номер объекта")? при таких данных там таких записей более миллиарда и они удаляются в общем случае в произвольном порядке. У нас ничего быстрее килла нет. TryCacheТ.е. нужно удалять порядка 300000 записей в секунду в течение часа. Это возможно физически? Не помрет э точно. А вот успеет или нет - это х/з... ... |
|||
:
Нравится:
Не нравится:
|
|||
17.07.2015, 10:05 |
|
[GlobalsDb] Консультация по удалению данных из GlobalsDb
|
|||
---|---|---|---|
#18+
TryCache , Можно попробовать сначала пробежать по глобалу с данными и удалить данные по id. А потом по всему глобалу с индексами и удалить все индексы, которые относятся к удалённым объектам. Аналог COS'овского $Query для обхода всего глобала -- это nextNodeSubscripts. Хотя тут уже упрётся в скорость проверки того, нужно ли удалять данный id. ... |
|||
:
Нравится:
Не нравится:
|
|||
17.07.2015, 10:32 |
|
[GlobalsDb] Консультация по удалению данных из GlobalsDb
|
|||
---|---|---|---|
#18+
TryCache, Мои 5 коп: 1.kill на весь indices 2.Пробегаем по objects и удаляем ненужные. 3.Пробегаем по оставшимся objects и строим indices. При удалении 90% данных должно быть быстрее. ... |
|||
:
Нравится:
Не нравится:
|
|||
17.07.2015, 11:43 |
|
[GlobalsDb] Консультация по удалению данных из GlobalsDb
|
|||
---|---|---|---|
#18+
TryCache, Разбивать на тома необязательно. Можно в objects и indices первым индексом сделать "номер тома". ... |
|||
:
Нравится:
Не нравится:
|
|||
17.07.2015, 11:49 |
|
[GlobalsDb] Консультация по удалению данных из GlobalsDb
|
|||
---|---|---|---|
#18+
DirksDRTryCache, Мои 5 коп: 1.kill на весь indices 2.Пробегаем по objects и удаляем ненужные. 3.Пробегаем по оставшимся objects и строим indices. При удалении 90% данных должно быть быстрее.Поддерживаю. Я тут поэкспериментировал на каше: Удаление данных из основного узла и из индексов >d GenerateData^tmp(1000000,$lb("k1","k2","k3","k4","k5","k6"),0) 1000000 Затрачено: 62.726327 сек. >d KillData^tmp(1,900000,0) 900000 Затрачено: 33.279532 сек.И по варианту предложенному DirksDR >d GenerateData^tmp(1000000,$lb("k1","k2","k3","k4","k5","k6"),0) 1000000 Затрачено: 56.564045 сек. >d KillData2^tmp(1,900000,0) 900000 Затрачено: 12.99051 сек. ;============================================================================= ; Генерируем данные ; to - количество генерируемых объектов ; lstInd - список индексов ;============================================================================= GenerateData(to=0,lstInd="",jrnl=1) N ind,i,j,len,start,end I 'to Q I '$LV(lstInd) Q S len=$LL(lstInd) S start=$ZH I 'jrnl d DISABLE^%NOJRN F i=1:1:to { W $C(13),i F j=1:1:len { S ind=$LG(lstInd,j) D SaveRandomData(i,ind) } } I 'jrnl d ENABLE^%NOJRN S end=$ZH W !,"Затрачено: ",end-start," сек." Q ;============================================================================= ; Сохраним произвольные данные в базу ;============================================================================= SaveRandomData(i,ind) N data S data = $RANDOM(+$H) D KILLOLDINDEX S ^tmpobjects(i,ind)=data S ^tmpindices(ind,data,i)="" Q ;============================================================================= ; Обновим индекс ;============================================================================= KILLOLDINDEX N old S old=$G(^tmpobjects(i,ind)) I old'="" { K ^tmpindices(ind,old,i) } Q ;============================================================================= ; Удаляем данные из ^tmpobjects и ^tmpindices ;============================================================================= KillData(from=1,to=0,jrnl=1) N obj,ind,start,end,data I 'to Q S start=$ZH I 'jrnl d DISABLE^%NOJRN F obj=from:1:to { W $C(13),obj I '$D(^tmpobjects(obj)) continue S ind="" F { S ind=$O(^tmpobjects(obj,ind)) Q:ind="" S data=$G(^tmpobjects(obj,ind)) I data'="" { K ^tmpindices(ind,data,obj) } K ^tmpobjects(obj,ind) } } I 'jrnl d ENABLE^%NOJRN S end=$ZH W !,"Затрачено: ",end-start," сек." Q ;============================================================================= ; Удаление по предложению DirksDR ;============================================================================= KillData2(from=1,to=0,jrnl=1) N obj,ind,start,end,data I 'to Q S start=$ZH I 'jrnl d DISABLE^%NOJRN K ^tmpindices F obj=from:1:to { W $C(13),obj I '$D(^tmpobjects(obj)) continue S ind="" F { S ind=$O(^tmpobjects(obj,ind)) Q:ind="" K ^tmpobjects(obj,ind) } } D REINDEX I 'jrnl d ENABLE^%NOJRN S end=$ZH W !,"Затрачено: ",end-start," сек." Q ;============================================================================= ; Переиндексируем ;============================================================================= REINDEX N obj,ind,data S obj="" F { S obj=$O(^tmpobjects(obj)) Q:obj="" S ind="" F { S ind=$O(^tmpobjects(obj,ind)) Q:ind="" S data=$G(^tmpobjects(obj,ind)) S:data'="" ^tmpindices(ind,data,obj)="" } } Q ;============================================================================= ... |
|||
:
Нравится:
Не нравится:
|
|||
17.07.2015, 12:14 |
|
[GlobalsDb] Консультация по удалению данных из GlobalsDb
|
|||
---|---|---|---|
#18+
TryCache, Вы предположили, что проблема в "прыгании по диску", но случайный доступ к данным - нормальный режим для M/Cache'-систем, если бы он был проблемой, они бы вообще не работали! Поэтому для начала стоило бы определить, что тормозит: диск или процессор, сделать это можно на основе хотя бы монитора производительности Windows. Если диск, можно попробовать увеличить кэш данных; не уверен, что GlobalsDB это позволит, есть ли у неё файл конфигурации cache.cpf. Потенциально это может быть и процессор, т.к. вы теряете скорость на каждом удалении узла: для GlobalsDB это каждый раз обмен Java-Cache' (пусть и быстрый, через callin), а в Cache' можно было бы написать бы $$-функцию (или метод класса), которая удалила бы все необходимые узлы за один вызов. ... |
|||
:
Нравится:
Не нравится:
|
|||
17.07.2015, 12:19 |
|
[GlobalsDb] Консультация по удалению данных из GlobalsDb
|
|||
---|---|---|---|
#18+
Alexey Maslov, Я от эксплуатирующей стороны. Загрузка проца практически нулевая. Диск горит красным пламенем. Из наблюдений: Когда выполняешь запрос к БД с условием, то он отбирает, например, 24 млн. записей за 5 мин. При этом одно ядро проца загружено под 70 %. Когда после этого отбора удаляешь все что отобрал (т.е. все ID есть), то загрузка проца иногда дергается на 5 %, а так все время 0%. И удаление идет в час по чайной ложке. Понятно что диск является тормозом, но мне кажется что-то в архитектуре не оптимально. ... |
|||
:
Нравится:
Не нравится:
|
|||
17.07.2015, 12:52 |
|
[GlobalsDb] Консультация по удалению данных из GlobalsDb
|
|||
---|---|---|---|
#18+
istar, Не знаток GlobalsDB, но что-то мне подсказывает, что она выполняет автоконфигурацию памяти при старте, а значит не вполне эффективно её использует. По косвенным признакам это может проявляться как малозаметный захват памяти при запуске СУБД. Поинтересуйтесь у знатоков GlobalsDB, допускает ли она ручную конфигурацию памяти. Если да, выставьте размер кэша данных (8K) примерно в полпамяти сервера, кэш программ >= 64MB. Настройки надо проконтролировать после перезапуска СУБД: не исчерпана ли свободная памяти, не началась ли активная подкачка. ... |
|||
:
Нравится:
Не нравится:
|
|||
17.07.2015, 13:47 |
|
[GlobalsDb] Консультация по удалению данных из GlobalsDb
|
|||
---|---|---|---|
#18+
Проверил свой вариант... Получается медленнее. create(N,kN,vN) n i k ^tmpD k ^tmpI k ^tmpI1 f i=1:1:N d obj q obj n k,id s id=$i(^tmpD) f k=1:1:kN d key q key n v,name s v=$r(vN) s ^tmpD(id,k)=v s ^tmpI(k,v,id)="" s name=$na(^tmpI(k,v,id)) s ^tmpI1(id,name)="" q delete(Percent) n max,i s t=$p($h,",",2) s max=^tmpD*(Percent/100)\1 f i=1:1:max d del1 ;f i=1:1:max d del2 w !,$p($h,",",2)-t q del2 n k s k="" f s k=$o(^tmpD(i,k)) q:k="" s v=^tmpD(i,k) k ^tmpI(k,v,i) k ^tmpD(i) q del1 n name s name="" f s name=$o(^tmpI1(i,name)) q:name="" k @name k ^tmpI1(i) k ^tmpD(i) q ... |
|||
:
Нравится:
Не нравится:
|
|||
17.07.2015, 14:46 |
|
[GlobalsDb] Консультация по удалению данных из GlobalsDb
|
|||
---|---|---|---|
#18+
Alexey MaslovПоинтересуйтесь у знатоков GlobalsDB, допускает ли она ручную конфигурацию памяти. 16817979 ... |
|||
:
Нравится:
Не нравится:
|
|||
17.07.2015, 14:52 |
|
[GlobalsDb] Консультация по удалению данных из GlobalsDb
|
|||
---|---|---|---|
#18+
TryCache2. Что может дать переход на Cache?Много чего: отключение журналирования, распараллеливание запросов, bitmap-индексы, sql-доступ, код непосредственно в СУБД ... TryCacheКстати, а насколько быстро работает kill ^indices(kx,vx,"номер объекта")?kill работает также быстро как и set, возможно даже быстрее, так как не нужно записывать данные, а лишь пометить узел как свободный. Но это Вам лучше расскажут непосредственно разработчики ядра. TryCacheпри таких данных там таких записей более миллиарда и они удаляются в общем случае в произвольном порядке. Т.е. нужно удалять порядка 300000 записей в секунду в течение часа. Это возможно физически?Смотря что понимать под "записью". 300000 обращений к БД (set/kill) в секунду - да (см. материалы , 2015.1 ), 300000 записей (бизнес-сущностей) в секунду - ответ уже не так однозначен. У Вас на одну бизнес-запись приходится ещё доп.индекс, а их ведь может быть больше одного, плюс могут быть связанные данные: итого на одну запись могут приходиться уже несколько set/kill, включая разного рода проверки, блокировки и т.п. TryCache2. для каждого ключа и значения метаданных иду в глобал indices, выполняю навигацию по ключу, затем значению, затем номеру object и делаю kill этого узла. Deleting Nodes with kill() and killNode() Т.е. можно указать ключи непосредственно в команде. Кроме того вместо kill можно использовать killNode, если потомков нет или их не нужно удалять. Кстати, сами данные можно хранить в стиле Caché, используя списки : сэкономите на количестве чтений. Например, так сделал Caché для отношения parent-children: s ^my.objectsD=4 s ^my.objectsD(1)="" s ^my.objectsD(1,"keys",1)=$lb("k1","v1") s ^my.objectsD(1,"keys",2)=$lb("k2","v2") s ^my.objectsD(2)="" s ^my.objectsD(2,"keys",3)=$lb("k2","v6") s ^my.objectsD(2,"keys",4)=$lb("k3","v4") s ^my.objectsD(3)="" s ^my.objectsD(3,"keys",5)=$lb("k1","v5") s ^my.objectsD(4)="" s ^my.objectsD(4,"keys",6)=$lb("k1","v1") s ^my.keysI("idxFind","k1","v1",1,1)="" s ^my.keysI("idxFind","k1","v1",4,6)="" s ^my.keysI("idxFind","k1","v5",3,5)="" s ^my.keysI("idxFind","k2","v2",1,2)="" s ^my.keysI("idxFind","k2","v6",2,3)="" s ^my.keysI("idxFind","k3","v4",2,4)=""Ещё данные можно не удалять физически, а лишь помечать как удалённые; реально же удалять потом в фоне и в несколько потоков. ... |
|||
:
Нравится:
Не нравится:
|
|||
17.07.2015, 16:30 |
|
[GlobalsDb] Консультация по удалению данных из GlobalsDb
|
|||
---|---|---|---|
#18+
servitЕщё данные можно не удалять физически, а лишь помечать как удалённые; реально же удалять потом в фоне и в несколько потоков. А как это сделать в GlobalsDb? Принимая во внимание, что есть два глобала и удалить нужно из двух мест? ... |
|||
:
Нравится:
Не нравится:
|
|||
17.07.2015, 16:35 |
|
[GlobalsDb] Консультация по удалению данных из GlobalsDb
|
|||
---|---|---|---|
#18+
TryCacheobjects - содержит перечень номеров объектов от 1 и далее как минимум до 50 млн. Под каждым объектом лежит набор ключей и значений. Набор ключей - это ограниченное множество строк (порядка 30). Обычно у каждого объекта есть порядка 20 ключей. Каждый конкретный ключ конкретного объекта содержит одно значение.Напоминает модель EAV: посмотрите SQL-индекс по элементам свойства-массива и Индексация неатомарных атрибутов . ... |
|||
:
Нравится:
Не нравится:
|
|||
17.07.2015, 16:51 |
|
[GlobalsDb] Консультация по удалению данных из GlobalsDb
|
|||
---|---|---|---|
#18+
TryCacheА как это сделать в GlobalsDb? Принимая во внимание, что есть два глобала и удалить нужно из двух мест?Добавить новое булево поле (удалено/не удалено), добавить на него индекс (для дальнейшего быстрого поиска удалённых записей) и учитывать новое поле при поиске. А насчёт многопоточности: 17044536 и 16610410 . ... |
|||
:
Нравится:
Не нравится:
|
|||
17.07.2015, 17:03 |
|
[GlobalsDb] Консультация по удалению данных из GlobalsDb
|
|||
---|---|---|---|
#18+
istar...мне кажется что-то в архитектуре не оптимально.Не в обиду: возможно, не оптимален выбор СУБД. У вас приличная нагрузка, похоже, будет даже после оптимизации алгоритмов, по-видимому и проект серьёзный, если даже служба эксплуатации есть. А используете СУБД, которую производитель уже года 2 как забросил... Понятно, что могут быть финансовые ограничения, но если вы используете только глобалы, возможно, все возможности Cache' вам пока и не к чему, подошёл бы "обычный" MUMPS; выбор невелик, но нельзя сказать, что его нет совсем (рекламировать и агитировать не буду). ... |
|||
:
Нравится:
Не нравится:
|
|||
17.07.2015, 18:23 |
|
|
start [/forum/topic.php?fid=39&msg=39009680&tid=1556616]: |
0ms |
get settings: |
9ms |
get forum list: |
13ms |
check forum access: |
3ms |
check topic access: |
3ms |
track hit: |
69ms |
get topic data: |
11ms |
get forum data: |
3ms |
get page messages: |
53ms |
get tp. blocked users: |
1ms |
others: | 270ms |
total: | 435ms |
0 / 0 |