Delphi - TSQLQuery оставляет процесс на MySQL даже после освобождения - PullRequest
2 голосов
/ 08 мая 2009

Я использую DBExpress в Delphi 2007 для подключения к серверу базы данных MySQL5 в сети.

Все работает нормально, пока я не попытаюсь загрузить большой объем данных. Я пытаюсь вставить 8000+ записей в базу данных, по одной в цикле, в цикле я передаю объект TSQLConection в функцию вместе с данными, которые нужно вставить.

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

+---------+------+-------------------------------+--------+---------+------+--------+-----------------------+
| Id      | User | Host                          | db     | Command | Time | State  | Info                  |
+---------+------+-------------------------------+--------+---------+------+--------+-----------------------+
| 2962500 | name | myispdomain.co.uk:27812       | data   | Sleep   |    3 |        | [NULL]                |
+---------+------+-------------------------------+--------+---------+------+--------+-----------------------+

Для каждого созданного мной объекта TSQLQuery существует одна запись, и если я пошагово выполняю код, то при запуске ExecSQL () можно увидеть новую запись. Я вызываю FreeAndNil в TSQLQuery и пытался вызвать Close перед его освобождением.

Мои настройки подключения MySQL следующие

   ConnectionName := 'MySQLConnection';
   DriverName     := 'MySQL';
   GetDriverFunc  := 'getSQLDriverMYSQL';
   KeepConnection := TRUE;
   LibraryName    := 'dbxmys30.dll';
   LoadParamsOnConnect := False ;
   LoginPrompt := FALSE;
   Name := 'mySQLConnection';
   VendorLib := 'LIBMYSQL.DLL';
   TableScope := [tsTable,tsView];

    Params.Add('DriverName=MySQL');
    Params.Add('HostName=www.sample.com');
    Params.Add('Database=data'); 
    Params.Add('User_Name=myuser'); 
    Params.Add('Password=mypassword'); 
    Params.Add('BlobSize=-1');
    Params.Add('ErrorResourceFile=');
    Params.Add('LocaleCode=0000');
    Params.Add('Compressed=False');
    Params.Add('Encrypted=True');

Если я установил для KeepConnection значение False, проблема исчезнет, ​​но время для выполнения запросов возрастет.

Есть ли способ обойти это?

Ответы [ 4 ]

3 голосов
/ 08 мая 2009

Не создавайте заново ни ваше соединение, ни ваш запрос. Используйте параметры для запроса; откройте соединение один раз, заполните параметры запроса, выполните его, закройте запрос (но не соединение), снова заполните параметры запроса и повторите его.

Примерно так (с использованием Advantage Database Server, но концепция та же):

// Both Create() calls should be followed by try..finally to ensure they're 
// cleaned up after. Omitted for brevity.
Conn := TAdsConnection.Create(nil);
// Configure connection parameters here
Conn.Open;

Qry := TAdsQuery.Create(nil);
Qry.AdsConnection := Conn;
Qry.SQL.Add('INSERT INTO SOMETABLE (COL1, COL2, COL3)');
Qry.SQL.Add('VALUES (:COL1, :COL2, :COL3)');
while not OtherTable.Eof do
begin
  Qry.ParamByName('COL1').AsInteger := OtherTable.FieldByName('COL1').AsInteger;
  Qry.ParamByName('COL2').AsString := OtherTable.FieldByName('COL2').AsString;
  Qry.ParamByName('COL3').AsDateTime := OtherTable.FieldByName('COL3').AsDateTime;
  Qry.ExecSQL;
  Qry.Close;
  OtherTable.Next;
end;
// Free query
Conn.Close;
// Free connection.
1 голос
/ 05 июня 2009
ConnectionName := 'MySQLConnection';
DriverName     := 'MySQL';
GetDriverFunc  := 'getSQLDriverMYSQL';
**KeepConnection := TRUE;**    //   <---------- this setting is persisting the database connection, change to false if you want to disconnect 
LibraryName    := 'dbxmys30.dll';
LoadParamsOnConnect := False ;
LoginPrompt := FALSE;
Name := 'mySQLConnection';
VendorLib := 'LIBMYSQL.DLL';
TableScope := [tsTable,tsView];

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

Команда ---> sleep указывает, что вы подключены к базе данных, но в данный момент не выполняется ни один запрос ( TSQLQuery и т. Д.). Фактически, если вы не создаете операцию с высокой нагрузкой, скорее всего, вы никогда не увидите ничего, кроме ' sleep '.

1 голос
/ 03 июня 2009

Вы можете создать большую вставку, что-то вроде:

Qry := TAdsQuery.Create(nil);
Qry.AdsConnection := Conn;
String qry = "INSERT INTO SOMETABLE (COL1, COL2, COL3)";
String values = "";

while not OtherTable.Eof do
begin
  values += "('"  + OtherTable.FieldByName('COL1').AsString + 
            "','" + OtherTable.FieldByName('COL1').AsString + 
            "','" + OtherTable.FieldByName('COL1').AsString + 
            "'),";
  OtherTable.Next;
end;

//to remove last ',' and add query terminator ";"
values = values.SubString(0, values.Length()-1) + ";"; 

qry = qry + values; //build the bulk insert

Qry.Add(qry);
Qry.ExecSQL;
//Qry.Close() no need of this, exec executes the command and leave, theres nothing to close
// Free query
Conn.Close;

оператор вставки должен выглядеть следующим образом: INSERT INTO (col1, col2, col3) значения ( 'Val1', 'значение2', 'val3'), ( 'Val1', 'значение2', 'val3'), ( 'Val1', 'значение2', 'val3'), ..., ( 'Знач1', 'знач2', 'val3');

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

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

В настоящее время я использую этот подход со вставками по 1000 строк (с 4-8 столбцами данных) без проблем, я тестирую его с 5000 строками, но выбрасываю исключение «потерянное соединение во время запроса», поэтому я оставил его в 1000. Я думаю, что при настройке сервера вы могли бы увеличить скорость передачи данных, предполагая, что компонент не является проблемой, в моем случае 1000 достаточно.

Наконец, я использую Builder C ++, а не Delphi, поэтому код, который я публикую, может содержать ошибки

-Jose

1 голос
/ 08 мая 2009

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

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