Обсуждение статьи "SQLite: нативная работа с базами данных на SQL в MQL5" - страница 8

 
Ilyas #:

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

По ошибке 4022, Вы получаете её при тестировании, если да, то в тестере имеется ограничение на размер файла экспорта -  1GB общий размер записанных на диск данных ?

Ошибка 4022 появляется без тестера, если указать в функцию DatabaseExport запрос вместо имени таблицы.

Размер базы меньше 10 Мб, планировал использовать этот функционал для копирования данных из базы на диске в базу в памяти.
 
Ilyas #:
Запрос должен работать, проверьте правильность путей

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

О результате напишу.

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

Тест для многопоточной записи в БД, запускать несколько копий скрипта, каждую копию можно пометить параметром expert_id.

#property copyright "Aliaksandr Hryshyn"
#property link      "https://www.mql5.com/ru/users/greshnik1"
#property version   "1.00"
#property script_show_inputs

enum e_E
  {
   e_0,
   e_1,
   e_2,
   e_3,
  };
input e_E expert_id=0;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   int db_handle=DatabaseOpen("tmp//test.sqlite",DATABASE_OPEN_READWRITE|DATABASE_OPEN_CREATE|DATABASE_OPEN_COMMON);
   const string sql_create=
      "CREATE TABLE IF NOT EXISTS Demo("
      "Id INTEGER PRIMARY KEY AUTOINCREMENT,"
      "Expert_id INTEGER,"
      "demo_0 TEXT(2000),"
      "demo_1 TEXT(2000),"
      "demo_2 TEXT(2000),"
      "demo_3 TEXT(2000),"
      "demo_4 TEXT(2000),"
      "demo_5 TEXT(2000),"
      "demo_6 TEXT(2000),"
      "demo_7 TEXT(2000)"
      ");";
   const string sql_add=
      "INSERT INTO Demo(Expert_id,demo_%s) VALUES(%s,'%s')";
   if(!DatabaseExecute(db_handle,sql_create))//Создание таблицы если отсутсвует
     {
      DatabaseClose(db_handle);
      Print("Ошибка:",GetLastError());
      Print("Запрос:",sql_create);
      return;
     }
   string s1;
   string sql;
   int err_counter=100;//Счётчик ошибок добавления записи
   for(int i1=0; i1<1000; i1++)
     {
      s1="";
      for(int i2=0; i2<100; i2++)
        {
         s1+=string(MathRand());
        }
      sql=StringFormat(sql_add,string(MathRand()%8),string(expert_id),s1);
      if(DatabaseExecute(db_handle,sql))//Добавляем запись в случайный столбец из набора demo_..
        {
         err_counter=100;
        }
      else
        {
         if(err_counter==0)
           {
            DatabaseClose(db_handle);
            Print(expert_id,": end");
            return;
           }
         else
           {
            Print(expert_id,": ",err_counter);
            err_counter--;
            i1--;
           }
        }
      if(IsStopped())
         break;
      Sleep(1);//Немного спим
     }
   DatabaseClose(db_handle);
  }

Всё правильно пишется:


Только сыпятся ошибки о блокировке:

...
2021.09.18 13:08:05.252 Test_sql (EURUSD,M1)    2: 80
2021.09.18 13:08:05.267 Test_sql (EURUSD,M1)    database error, database is locked
2021.09.18 13:08:05.267 Test_sql (EURUSD,M1)    2: 79
2021.09.18 13:08:05.283 Test_sql (EURUSD,M1)    database error, database is locked
2021.09.18 13:08:05.283 Test_sql (EURUSD,M1)    2: 78
2021.09.18 13:08:05.299 Test_sql (EURUSD,M1)    database error, database is locked
2021.09.18 13:08:05.299 Test_sql (EURUSD,M1)    1: 100
2021.09.18 13:08:05.315 Test_sql (EURUSD,M1)    database error, database is locked
2021.09.18 13:08:05.315 Test_sql (EURUSD,M1)    1: 99
...

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

 
Ilyas #:
Запрос должен работать, проверьте правильность путей

1. Проверил ATTACH DATABASE действительно работает! 

Проблема была в следующем, я пытался присоединить к базе созданной в оперативной памяти, физическую базу в этом случае ATTACH DATABASE не работает, а если присоединяем оперативную базу к физической, то работает!

Мне этого вполне хватает для создания копии базы в оперативной памяти, что ускоряет вычисления))


2. Решил пойти дальше... и проверить ещё одну возможность, а именно:

ATTACH DATABASE 'file:memdb1?mode=memory&cache=shared' AS M;

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

Во 1 я могу к ней присоединиться из любой MQL5 программы, советник, индикатор, скрипт, сервис,

Во 2 данные в ней сохраняются навсегда, даже после перезагрузки остаются, уже подумал..., что может база создается физически, но поиском по имени *memdb1* ничего не нашлось

П.С. Возможно из-за того, что я создавал базу из индикатора, а он работает в общем потоке?


Так то это крутая фишка, но вот хотелось бы узнать останется ли она в терминале в следующих обновлениях и после моего сообщения об этом?:))

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


В таком исполнении удаляется, после завершения MQL5 программы:

ATTACH DATABASE ':memory:?cache=shared' AS M;

ATTACH DATABASE ':memory:' AS M;

 

Добрый день!

Вопрос к разработчикам MT, возможно ли отключить атрибут Shared у библиотеки SQLite, опытным путем в других проектах определил, что именно в этом причина ошибки: database error, database is locked.

Если обращение происходит из разных потоков к базе, то это все решается на уровне самой базы, а именно выполнив эти два запроса сразу после открытия базы:

1. PRAGMA journal_mode = WAL;

2. PRAGMA busy_timeout = 1000;

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

 
Daniil Kurmyshev #:

Добрый день!

Вопрос к разработчикам MT, возможно ли отключить атрибут Shared у библиотеки SQLite, опытным путем в других проектах определил, что именно в этом причина ошибки: database error, database is locked.

Если обращение происходит из разных потоков к базе, то это все решается на уровне самой базы, а именно выполнив эти два запроса сразу после открытия базы:

1. PRAGMA journal_mode = WAL;

2. PRAGMA busy_timeout = 1000;

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

при этом скорость запросов упадёт, а требования по памяти увеличатся...

а так всё Ок :-) 

SQLite заточен под один поток или один писатель и много читателей. Много писателей не про него, про это другие базы которые не "лайт"

это безотносительно MQL - вот просто так оно и есть на самом деле и везде
 

Maxim Kuznetsov #:

SQLite заточен под один поток или один писатель и много читателей. Много писателей не про него, про это другие базы которые не "лайт"

это безотносительно MQL - вот просто так оно и есть на самом деле и везде

И это ещё одна причина, по которой хотелось бы увидеть работу с PostgreSQL.

 
JRandomTrader #:

И это ещё одна причина, по которой хотелось бы увидеть работу с PostgreSQL.

так сделайте, в чём проблема ? DLL разрешите и пользуйтесь

не пользователь PostgreSQL, но MySQL,MonetDB, SQL Relay и ODBC использовал из mt5.

Когда есть потребность, проще и быстрее сделать самому, чем ждать милостей от природы

 
Maxim Kuznetsov #:

при этом скорость запросов упадёт, а требования по памяти увеличатся...

а так всё Ок :-) 

SQLite заточен под один поток или один писатель и много читателей. Много писателей не про него, про это другие базы которые не "лайт"

это безотносительно MQL - вот просто так оно и есть на самом деле и везде

Возможно Вы не умеете готовить SQLite, в ней нет узких мест, а именно таких как единый сервер TCP, который разруливает все поступающие потоки, этот сервер есть во всех остальных базах данных...

SQLite это подарок, если ее правильно приготовить, то скорость запросов на запись будет выше чем у Redis, а про чтение само собой, при этом когда вы читаете из базы потоки друг друга не блокируют и все происходит максимально быстро упираясь в ваши ресурсы системы, также вам дается возможность разворачивать БД в оперативной памяти при этом не важно каким образом, при создании, а можно через специальное ПО создать диск в оперативной памяти и на нем разместить БД и создать символическую ссылку, если например использовать для советников, что обеспечит наивысочайшую скорость доступа, конечно тоже самое можно сделать и в других БД, но это не будет иметь смысла, так как у них есть узкое место, а именно их TCP сервер, даже NOSQL база Redis не сможет дать вам той скорости, которую дает SQLite при правильной настройке.

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

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

Решение разработчиков MQL c одной стороны мне понятно, но с другой не очень, так как я имею большой опыт работы с платформой и не встречал случаев, чтобы кто либо запускал более 200 потоков одновременно, во 1 в терминале ограничение на 100 графиков, следовательно можно к каждому прикрепить по 1 советнику и по 1 скрипту, но можно также ещё запускать сервисы по кол-ву сервисов думаю ограничения нет, но это как минимум не удобно и ресурсозатратно в любом случае, а если бы эти потоки открывали свои собственные соединения с БД это капля в море по памяти относительно той, что уже выделили для запуска...

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

Уважаемые разработчики, пожалуйста уберите общий кэш или тыкните меня носов в то что я упустил.

Вырвал суть про "SQLite Shared-Cache" из официальной документации:

"This can significantly reduce the quantity of memory and IO required by the system."

Вот ссылка, почитайте.