Форум » LetoDB, HbNetio. » А работает ли механизм транзакций letodb » Ответить

А работает ли механизм транзакций letodb

PSP: в udf-функциях?

Ответов - 44, стр: 1 2 3 All

Pasha: Увы, нет. Механизм транзакции таков: все изменения накапливаются на клиенте без передачи на сервер, и только по LETO_COMMITTRANSACTION пакетом передаются на сервер, где и выполняются. Поскольку вызовы leto_udf сразу передаются на сервер и сразу выполняются, вписать их в механизм транзакций не представляется возможным.

PSP: Жаль. Подумалось, что было бы неплохо использовать свойство транзакции "либо выполняется целиком, либо не выполняется вообще" при записи из udf-функции, но, видимо, там уже напрямую работает rdd и "влезть" в этот процесс без переделки rdd не получится. Спасибо.

nbatocanin: Команда Leto_BeginTransaction() работает, только если DBF открыт с LetoDB. Например, это не работает: USE Test VIA "DBFNTX" NEW Leto_BeginTransaction() // Syntax error Почему? Это очень неудобно для универсальной процедурой которая копирует записи из локального DBF на сервер.

sashaBG: С транзакциями надо соблюдать следующие правила : 1. Использовать их только в зонах действия RDD "LETO" , другими словами после RDDSETDEFAULT( "LETO" ) а потом для взаимодействия между файлми разных RDD использовать алиасное имя 2. До закрытия транзакции нельзя использовать команду COMMIT. ( получится Sintax error) Может Паша посоветует еще чего то .

Pasha: Надо просто вызывать Leto_BeginTransaction() с рабочей областью, открытой через leto Пример: letotab->( Leto_BeginTransaction() )

nbatocanin: Я знаю, что это может решить проблему, но мне интересно, почему Leto_BeginTransaction() использует открытый DBF? Объяснение: в программе у меня есть следующие части: SELECT Tmp TransBegin() WHILE !Eof() TransProc() LetoTab->(DBAppend()) LetoTab->f1 := f1 LetoTab->f2 := f2 ... SKIP END DO TransEnd() Универсальные функции (TransBegin, TransProc и TransEnd) хочу, чтобы использовать во всей программы. Функция TransBegin на основе записи TMP (DBFNTX) устанавливает параметры и призывает Leto_BeginTransaction. Но TransBegin "не знает" за LetoTab, поэтому я вынужден передать и этот параметр. Вот почему я хочу знать, если он играет роль в Leto_BeginTransaction?

Pasha: Такое поведение связано с тем, что функции Leto_BeginTransaction необходимо знать, для какого соединения (сервера) letodb будет транзакция. Соединений же может быть несколько. Вот это соединение и берется из текущей рабочей области. А если эта р/о не leto, то генерируется ошибка.

nbatocanin: Спасибо! Я буду использовать любую открытую DBF. (Это не имеет большое значения, но я думаю, что это должно быть изменено).

nbatocanin: Когда я начал использовать транзакции, моя программа начала работать гораздо медленнее. Так что я сделал некоторые тесты. Тестовая программа копирует локальный DBF в таблице на сервере. Я обнаружил, что моя программа работает намного медленнее, чем Андреев DbfToServer. Например, таблицы 30000 записей: DbfToServer: 11 с моя программа: 65 с Чтобы скопировать, используя тот же алгоритм. После многих испытаний, я нашел разницу: моя DBF таблицы на сервере имеет индекс. Когда индекс выключен, программа значительно ускоряется. Это нормально, LetoDB требуется больше времени, чтобы обновить индекс. Но когда выключается транзакции, программа становится в несколько раз быстрее: 19 с! Почему? Это не логично. Тогда я попытался изменить размер буфера для транзакции. Я обменял буфер от 1000 до 50.0000, но я не заметил существенной разницы в скорости. Лучшие результаты имеют для буфера 3000-4000. DbfToServer определять размер буфера 1000 x RecordSize, но использует только 70%. Например, если размера данных 1500, тогда буфер 1.500.000, что я думаю, это слишком много. Что бы вы посоветовали?

nbatocanin: Продолжаю тестирования с индексами. Я сделал программу, которая копирует Test0 DBF (10000 записей) с локального диска на сервер в DBF Test2. Копирование выполняется следующим образом: 1. Тест2 не имеет индексов, без транзакции (-) 2. Тест2 имеет индексы, без транзакции (I) 3. Тест2 не имеет индексы, транзакции включены (T) 4. Тест2 имеет индексы, транзакции включены (I+T) Программа и результаты можно найти здесь: https://drive.google.com/folderview?id=0BwEGIJ1QfjrKY1NNLWRheENyUkE&usp=sharing Результаты показывают, что программа действительно замедляет, когда включены транзакции и индексы: [pre] Records - I T I+T ------------------------------- 90000 2.3 3.7 1.5 2.6 170000 2.2 2.3 1.5 3.0 730000 2.2 2.2 1.6 7.6 1450000 2.2 2.3 2.1 11.0 2010000 2.2 2.4 2.0 12.3 2050000 2.2 2.5 2.0 14.3[/pre] Почему это происходит? Результаты показывают, что операции и индексы не является проблемой, когда они не используются вместе.

Pasha: А в letodb.ini на сервере случайно нет строки: Share_Tables = 1 ?

nbatocanin: Да. Но я проверил вариант Share_Tables = 0 и получил аналогичные результаты: [pre2]Records - Ind Tran Ind+T ------------------------------- 2090000 2.3 10.2 1.5 14.5 2130000 2.1 2.4 1.8 14.6 2170000 2.2 2.3 1.8 14.4 2210000 2.5 2.4 1.9 15.3 2250000 2.3 2.2 2.0 14.8 2290000 2.5 2.7 2.1 15.8 2330000 2.4 2.5 2.0 14.9[/pre2]

Pasha: Что-то у меня не получается такой результат. А в prg по ссылке нет вообще операций с транзакциями. Я сделал свой тест с индексом: PROC Main Local cPath, nSec, i, nn := 0 Local lTr := .t. request leto, DBFCDX RddSetDefault ("LETO") cPath := "//127.0.0.1:2812/" Leto_Connect (cPath) USE (cPath + "wopl") SHARED NEW SET INDEX TO (cPath + "wopl") use wop new via "DBFCDX" go top nSec := Seconds() if lTr wopl->(leto_BeginTransaction(32768)) endif while ! eof() wopl->(dbAppend()) for i := 1 to fcount() wopl->(FieldPut(i, wop->(FieldGet(i)))) next if nn % 10000 == 0 .and. lTr wopl->(leto_CommitTransaction()) wopl->(leto_BeginTransaction(32768)) endif if ++nn == 100000 exit endif skip enddo if lTr wopl->(leto_CommitTransaction()) endif ? Seconds() - nSec // 23.87 -T+I // 7.32 +T+I RETURN Результат работы для 100000 записей: 23.87 сек без транзакций 7.32 сек с транзакциями Т.е. передача файла, если использовать транзакции, выполняется примерно в 3 раза быстрее. Одну большую транзакцию я разбил на небольшие по 10000 записей. Может быть, выложите свой тест, в котором получается медленный результат при использовании транзакций ?

nbatocanin: DBF Wop (локальный) не должен быть больше, чем 10.000 записей. Основная проблема находится в WopL (на сервере). DBF на сервере должен иметь более 500.000 записей и минимум два индекса. Когда есть меньше записей или индекс, скорость увеличивается. Я пробовал ваш тест программ (Wop = 10k записей): [pre2]WopL Index -T+I +T+I -------------------------------------- 10k 1 4.2 3.9 10k 2 4.2 4.5 2M 1 4.4 7.4 2M 2 4.3 13.0 [/pre2]

Pasha: Большие dbf можно взять здесь: http://www.gnivc.ru/html/gnivcsoft/KLADR/Base.7z Я взял doma.dbf, более 2-х млн.записей, размер более 200MB Создал много (9) индексов: имя тэга ключ code 'CODE' namec 'SUBSTR(CODE,1,11)+UPPER(NAME)' nasel 'SUBSTR(CODE,1,11)' nameg 'SUBSTR(CODE,1,8)+UPPER(NAME)' namemp 'SUBSTR(CODE,1,5)+UPPER(NAME)' ocato 'OCATD' gni 'GNINMB' scode 'SUBSTR(CODE,1,2)' s_code 'SUBSTR(CODE,1,15)' Прогнал по сети тест с копированием 100000 записей на сервер letodb без транзакций, и с транзакциями по 10000 записей Результат теста: Без транзакций: 154 сек С транзакциями: 44 сек Вот чуть измененный тест: PROC Main Local cPath, nSec, i, nn := 0 Local cNm := 'doma' Local lTr := .t. request leto, DBFCDX RddSetDefault ("LETO") cPath := "//10.0.0.1:2812/" //cPath := "//127.0.0.1:2812/" Leto_Connect (cPath) USE (cPath + cNm) alias db_srv SHARED NEW SET INDEX TO (cPath + cNm) use (cNm) alias db_loc new via "DBFCDX" go top nSec := Seconds() if lTr db_srv->(leto_BeginTransaction(32768)) endif while ! eof() db_srv->(dbAppend()) for i := 1 to fcount() db_srv->(FieldPut(i, db_loc->(FieldGet(i)))) next if nn % 10000 == 0 ? nn if lTr db_srv->(leto_CommitTransaction()) db_srv->(leto_BeginTransaction(32768)) endif endif if ++nn == 100000 exit endif skip enddo if lTr db_srv->(leto_CommitTransaction()) endif ? lTr, Seconds() - nSec RETURN

nbatocanin: Я использую NTX индексы, предполагают, что это может быть проблема. Программа с CDX индексов работать быстрее - время сокращается с 13 до 4,4 секунд. Вы можете проверить это? К сожалению, я должен использовать NTX индексов.

Pasha: Ну никак не получается у меня эффект медленной транзакции. Для ntx копирование 15000 записей с 9 индексами: без транзакции примерно 7 сек, с транзакциями - 6 сек. letodb.ini: DataPath = e:/db EnableFileFunc = 1 EnableAnyExt = 1 Optimize = 1 AutOrder = 1 Default_Driver = NTX тест: PROC Main Local cPath, nSec, i, nn := 0 Local cNm := 'doma' Local lTr := .t. Field Code, OCATD, GNINMB request leto, DBFCDX RddSetDefault ("LETO") //cPath := "//10.0.0.1:2812/" cPath := "//127.0.0.1:2812/" Leto_Connect (cPath) USE (cPath + cNm) alias db_srv SHARED NEW if ! leto_File(cPath+cNm+'.ntx') ? 'Indexing...' index on CODE to doma1 index on SUBSTR(CODE,1,11) to doma2 index on SUBSTR(CODE,1,11) to doma3 index on SUBSTR(CODE,1,8) to doma4 index on SUBSTR(CODE,1,5) to doma5 index on OCATD to doma6 index on GNINMB to doma7 index on SUBSTR(CODE,1,2) to doma8 index on SUBSTR(CODE,1,15) to doma9 endif SET INDEX TO (cPath + cNm + '1') SET INDEX TO (cPath + cNm + '2') SET INDEX TO (cPath + cNm + '3') SET INDEX TO (cPath + cNm + '4') SET INDEX TO (cPath + cNm + '5') SET INDEX TO (cPath + cNm + '6') SET INDEX TO (cPath + cNm + '7') SET INDEX TO (cPath + cNm + '8') SET INDEX TO (cPath + cNm + '9') dbSetOrder(1) use (cNm) alias db_loc new via "DBFCDX" go top nSec := Seconds() if lTr db_srv->(leto_BeginTransaction(32768)) endif while ! eof() db_srv->(dbAppend()) for i := 1 to fcount() db_srv->(FieldPut(i, db_loc->(FieldGet(i)))) next if nn % 10000 == 0 ? nn if lTr db_srv->(leto_CommitTransaction()) db_srv->(leto_BeginTransaction(32768)) endif endif if ++nn == 15000 exit endif skip enddo if lTr db_srv->(leto_CommitTransaction()) endif ? lTr, Seconds() - nSec RETURN

nbatocanin: LetoDB.ini: Default_Driver = NTX EnableFileFunc = 1 Optimize = 1 сервер: Windows Server 2003, 100Mb LAN рабочая станция: Windows 7 SET INDEX TO (cPath + cNm + '1') SET INDEX TO (cPath + cNm + '2') SET INDEX TO (cPath + cNm + '3') SET INDEX TO (cPath + cNm + '4') SET INDEX TO (cPath + cNm + '5') SET INDEX TO (cPath + cNm + '6') SET INDEX TO (cPath + cNm + '7') SET INDEX TO (cPath + cNm + '8') SET INDEX TO (cPath + cNm + '9') dbSetOrder(1) Я думаю, что это только открывает последний индекс. Попробуйте: SET INDEX TO (cPath + cNm + '1'), (cPath + cNm + '2'), (cPath + cNm + '3'), ... Я попробую тест на другом сервере. Спасибо за помощь!

Pasha: Да, я почти сразу заметил, что пропустил ADDITIVE. Но и с этой опцией результат не изменился. Попробуйте мой тест с файлом по ссылке, что я дал. Может быть, в вашем тесте есть еще какие-нибудь особенности.

nbatocanin: результаты: без транзакций : 8.5 С транзакциями: 6.5 Это нормальный результат. Тогда я изменил DBF, добавил числовое поле ID (N10), заполнил ID = RecNo(). После этого изменил тестовую программу таким образом, чтобы она использует только два индекса: index on ID to doma1 index on Upper(NAME) to doma2 Потом я получил следующие результаты: без транзакции: 6.3 С транзакциями: 9.0 Когда я добавил третий числовой индекс: index on ID to doma3 получил: без транзакции: 6.4 С транзакциями: 11.2 Кажется, что проблема в числовых полях в индексе.

Pasha: Все равно у меня не получается медленная транзакция Без транзакции результат 24.65 сек С транзакцией: 4.96 сек Служба letodb установлена на компьютере с win7 letodb.ini: DataPath = e:/ EnableFileFunc = 1 EnableAnyExt = 1 Optimize = 1 AutOrder = 1 Default_Driver = NTX #Share_Tables = 1 Вот мой тест: PROC Main Local cPath, nSec, i, nn := 0 Local cNm := 'doma' Local lTr := .t. Field Code, OCATD, GNINMB, ID, Name request leto, DBFCDX RddSetDefault ("LETO") cPath := "//10.0.0.1:2812/" //cPath := "//127.0.0.1:2812/" Leto_Connect (cPath) USE (cPath + cNm) alias db_srv SHARED NEW if ! leto_File(cPath+cNm+'1.ntx') ? 'Indexing...' index on ID to doma1 index on Upper(NAME) to doma2 index on ID to doma3 endif SET INDEX TO (cPath + cNm + '1') additive SET INDEX TO (cPath + cNm + '2') additive SET INDEX TO (cPath + cNm + '3') additive dbSetOrder(1) use (cNm) alias db_loc new via "DBFCDX" go top nSec := Seconds() if lTr db_srv->(leto_BeginTransaction(32768)) endif while ! eof() db_srv->(dbAppend()) for i := 1 to fcount() db_srv->(FieldPut(i, db_loc->(FieldGet(i)))) next if nn % 10000 == 0 ? nn if lTr db_srv->(leto_CommitTransaction()) db_srv->(leto_BeginTransaction(32768)) endif endif if ++nn == 15000 exit endif skip enddo if lTr db_srv->(leto_CommitTransaction()) endif ? lTr, Seconds() - nSec RETURN

nbatocanin: Очень странно. Паша, я тестировал вашу программу на трех разных серверах (Win 2003, Win 7, Win 2012), и получил следующие результаты: [pre] Tr NoTr ------------------ s1 12.3 6.7 s2 8.4 6.2 s3 14.3 5.1 [/pre] Я бы попросить кого-нибудь, чтобы попробовать программу - на этом адресе тестовая программа: testhttps://drive.google.com/file/d/0BwEGIJ1QfjrKS1RYaEFGMXB0QzA/view?usp=sharing

Andrey: nbatocanin пишет: чтобы попробовать программу Попробовал. Сервер в инете Win2008, вот результат: Indexing... 0 10000 .F. 322.13 Indexing... 0 10000 .T. 2.36

nbatocanin: Есть ли функция Leto_CommitTransaction выполняет COMMIT? Необходимо ли выполнить COMMIT после Leto_CommitTransaction? Ненад

Pasha: COMMIT желательно делать до выполнения Leto_CommitTransaction()

nbatocanin: Я сделал новые тесты на несколько серверов (10+). Сначала я попробовал тест, который сделал Паша. Результаты (для меня) были очень странные. Время выполнения очень меняется (почему я взял среднее значение из нескольких тестов). На некоторых серверах, программа выполняется быстрее без транзакции, но в некоторых наоборот. Я думаю, время зависит от скорости диска и Windows кэширования. Например, тест на одном сервере с двумя дисками (HDD и SSD): [pre2] Tran NoTr --------------- HDD 3.9 1.1 SDD 1.4 1.8 [/pre2] Но этот тест не выполняют COMMIT в конце программы, так что я сделал новый тест, который использует функцию Leto_Commit(): [pre2]IF lTrans Leto_CommitTransaction() END IF Leto_Commit() [/pre2] Я также сделал тест так, чтобы больше соответствовать реальной ситуации. Вот тестовая программа: GoogleDrivehttps://drive.google.com/open?id=0BwEGIJ1QfjrKT2RLdlNvV2NwUHc Новый тест показал, что работать с транзакции (+COMMIT) всегда быстрее, чем без: [pre2] Tran NoTr --------------- S1 2.5 8.2 S2 1.1 5.6 S3 4.6 10.6 S4 1.1 0.8 [/pre2] Исключением является сервер с SSD (S4).

nbatocanin: Как ускорить транзакциу, которая имеет команду DELETE?. Например: [pre2]Leto_BeginTransaction() // SEEK nOldId WHILE nOldId == d_id DELETE SKIP END DO // ... // Leto_CommitTransaction()[/pre2] Эта программа работает в 20 раз быстрее без транзакции. Программа исключить около 25.000 записей.

Dima: nbatocanin пишет: WHILE nOldId == d_id Про ускорение не скажу а вот код надо подправить. [pre2] IF DBSEEK( nOldId ) WHILE nOldId == d_id .and. !eof() DELETE SKIP END DO ENDIF [/pre2] Хотя можно ускорить так (без проверки ключа) [pre2] IF DBSEEK( nOldId ) dbOrderInfo(DBOI_SCOPEBOTTOM,,,nOldId)) DO WHILE !eof() DELETE SKIP END DO dbOrderInfo(DBOI_SCOPEBOTTOMCLEAR) ENDIF [/pre2]

nbatocanin: Попробовал, но не имеют никакого эффекта. Я также попробовал DELETE WHILE. Без транзакции: 1.67 С транзакцией: 80.00

Pasha: nbatocanin пишет: Как ускорить транзакциу, которая имеет команду DELETE?. Например: Leto_BeginTransaction() // SEEK nOldId WHILE nOldId == d_id DELETE SKIP END DO // ... // Leto_CommitTransaction() Эта программа работает в 20 раз быстрее без транзакции. Программа исключить около 25.000 записей. Немного видоизмените алгоритм: Leto_BeginTransaction() // i := 0 SEEK nOldId WHILE nOldId == d_id DELETE i ++ if i%1000 == 0 Leto_CommitTransaction() Leto_BeginTransaction() endif SKIP END DO Leto_CommitTransaction() Причина замедления транзакции для большого количества записей следующая: после получения новых данных с сервера по команде skip выполняется проверка, есть ли новая полученная запись в данных транзакции. Если запись есть, то она формируется по данным из буфера транзакции. После каждой итерации буфер транзакции увеличивается, и проверка выполняется каждый раз медленнее. В этом случае такая проверка и не нужна, поскольку новая запись заведомо не присутствует в буфере транзакции, но в общем случае она нужна.

nbatocanin: Спасибо Пашa, это частично решило проблему. Но программа без транзакции по-прежнему работает быстрее (4.5sek; Без: 1.5сек). Можно ли как-то исключить эту проверку?

nbatocanin: Pasha пишет: Причина замедления транзакции для большого количества записей следующая: после получения новых данных с сервера по команде skip выполняется проверка, есть ли новая полученная запись в данных транзакции. Если запись есть, то она формируется по данным из буфера транзакции. После каждой итерации буфер транзакции увеличивается, и проверка выполняется каждый раз медленнее. В этом случае такая проверка и не нужна, поскольку новая запись заведомо не присутствует в буфере транзакции, но в общем случае она нужна. Паша, можно ли как-то выключение этой проверки? У меня есть большие транзакции, в которых нет дубликатов. Спасибо, Ненад

Pasha: Сегодня вечером сделаю обновление

SergKis: Pasha В старой версии leto (клиент), мы сделали (стало надежнее, стабильнее):[pre2] static BOOL lNewCritSect=TRUE; CRITICAL_SECTION CritSect; #define CS_EN() EnterCriticalSection( &CritSect ) #define CS_LV() LeaveCriticalSection( &CritSect ) long int leto_DataSendRecv( LETOCONNECTION * pConnection, const char * sData, ULONG ulLen ) { long int n = 0; if( lNewCritSect ) { InitializeCriticalSectionAndSpinCount(&CritSect, 0x00000400); // TryEnterCriticalSection( &CritSect ); lNewCritSect = FALSE; } CS_EN(); if( hb_ipSend( pConnection->hSocket, sData, (ulLen)? ulLen:strlen(sData), -1 ) > 0 ) { n = leto_Recv( pConnection->hSocket ); // return 0; } CS_LV(); // return leto_Recv( pConnection->hSocket ); return n; } [/pre2] в новой также планируем сделать, пока на новой не работаем (только тестируем)

SergKis: PS что то я с разделом промахнулся - это (выше) к "Internal Error" относиться. Sory

nbatocanin: long int leto_DataSendRecv( LETOCONNECTION * pConnection, const char * sData, ULONG ulLen ) Можете ли вы объяснить, что делает эта функция?

SergKis: nbatocanin пишет:что делает эта функция? как я понимаю, она посылает команды серверу и ждет их выполнения

nbatocanin: Pasha пишет: Сегодня вечером сделаю обновление работает, спасибо!

nbatocanin: Почему это не работает? USE Test NEW Leto_BeginTransaction () s := Select() FLock() (s)->(DBUnlock()) // Syntax error Leto_CommitTransaction ()

Pasha: Во время транзакции нельзя выдавать dbUnlock() Надо или разблокировать данные после LETO_COMMITTRANSACTION(). либо выдать LETO_COMMITTRANSACTION(.t.), задав параметр lUnlockAll

nbatocanin: А почему это не работает: USE Test1 SHARED NEW RLock () USE Test2 SHARED NEW RLock () Leto_BeginTransaction () Test2->t_name := "abc" Leto_CommitTransaction () Test1->t_log := .T. COMMIT // Error LETO/1022 Lock required

nbatocanin: Как я понимаю, транзакця разблокирует все таблицы, даже те, которые не упоминаются в транзакции?

Pasha: nbatocanin пишет: Как я понимаю, транзакця разблокирует все таблицы, даже те, которые не упоминаются в транзакции? Да, это так. Но у функции leto_CommitTransaction есть параметр lUnlockAll, и при вызове: leto_CommitTransaction( .F. ) разблокировка выполняться не будет. Почему так сделано - я точно не скажу, так как транзакции и этот фрагмент кода реализовывал не я.

nbatocanin: Жаль, что работает таким образом, я думаю, что сейчас транзакции самая слабая часть Letodb. Большое спасибо!



полная версия страницы