MQL5의 SQLite: 새로운 기능 및 성능 테스트

 

빌드 2265에서 SQLite 3.30.1을 기반으로 하는 일반 데이터베이스 기능을 구현했습니다.


데이터베이스는 DATABASE_OPEN_MEMORY 플래그를 사용하여 디스크와 메모리에만 보관할 수 있습니다. DatabaseTransactionBegin/Commit/Rollback 트랜잭션에서 대규모 삽입/변경을 래핑하면 작업 속도가 수백 배 빨라집니다.

가능한 한 성능에 중점을 두었으므로 다음은 LLVM 9.0.0 대 MQL5 테스트의 결과입니다. 시간(밀리초)은 낮을수록 좋습니다.
Windows 10 x64, Intel Xeon  E5-2690 v3 @ 2.60GHz
                                                        LLVM   MQL5
---------------------------------------------------------------------------------
Test  1: 1000 INSERTs:                                 11572   8488
Test  2: 25000 INSERTs in a transaction:                  59     60
Test  3: 25000 INSERTs into an indexed table:            102    105
Test  4: 100 SELECTs without an index:                   142    150
Test  5: 100 SELECTs on a string comparison:             391    390
Test  6: Creating an index:                               43     33
Test  7: 5000 SELECTs with an index:                     385    307
Test  8: 1000 UPDATEs without an index:                   58     54
Test  9: 25000 UPDATEs with an index:                    161    165
Test 10: 25000 text UPDATEs with an index:               124    120
Test 11: INSERTs from a SELECT:                           84     84
Test 12: DELETE without an index:                         25     74
Test 13: DELETE with an index:                            70     72
Test 14: A big INSERT after a big DELETE:                 62     66
Test 15: A big DELETE followed by many small INSERTs:     33     33
Test 16: DROP TABLE: finished.                            42     40

MQL5의 속도는 최고의 컴파일러 중 하나를 사용하는 네이티브 C++에서와 절대적으로 동일합니다. 재생 벤치마크 패키지가 첨부되었습니다.


또한 레코드를 구조로 직접 읽을 수 있는 고유한 DatabaseReadBind 기능을 구현하여 대량 작업을 단순화하고 속도를 높였습니다.

다음은 간단한 예입니다.

 struct Person
  {
   int                id;
   string             name;
   int                age;
   string             address;
   double             salary;
  };

//+------------------------------------------------------------------+
//| Test                                                             |
//+------------------------------------------------------------------+
bool TestDB( string filename, int flags)
  {
   int db;
//--- open
   db=DatabaseOpen(filename,flags);
   if (db== INVALID_HANDLE )
     {
       Print ( "DB: " ,filename, " open failed with code " , GetLastError ());
       return ( false );
     }
//--- create a table
   if (!DatabaseTableExists(db, "COMPANY" ))
       if (!DatabaseExecute(db, "CREATE TABLE COMPANY("
                           "ID INT PRIMARY KEY     NOT NULL,"
                           "NAME           TEXT    NOT NULL,"
                           "AGE            INT     NOT NULL,"
                           "ADDRESS        CHAR(50),"
                           "SALARY         REAL );" ))
        {
         Print ( "DB: " ,filename, " create table failed with code " , GetLastError ());
         DatabaseClose(db);
         return ( false );
        }
//--- insert data
   if (!DatabaseExecute(db, "INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) VALUES (1, 'Paul', 32, 'California', 20000.00 ); "
                       "INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) VALUES (2, 'Allen', 25, 'Texas', 15000.00 ); "
                       "INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) VALUES (3, 'Teddy', 23, 'Norway', 20000.00 );"
                       "INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) VALUES (4, 'Mark', 25, 'Rich-Mond ', 65000.00 );" ))
     {
       Print ( "DB: " ,filename, " insert failed with code " , GetLastError ());
      DatabaseClose(db);
       return ( false );
     }
//--- prepare the request
   int request=DatabasePrepare(db, "SELECT * FROM COMPANY WHERE SALARY>15000" );

   if (request== INVALID_HANDLE )
     {
       Print ( "DB: " ,filename, " request failed with code " , GetLastError ());
      DatabaseClose(db);
       return ( false );
     }
//--- выводим записи
   Person person;

   for ( int i= 0 ; DatabaseReadBind(request,person); i++)
       Print (i, ":  " ,person.id, " " , person.name, " " ,person.age, " " ,person.address, " " ,person.salary);

   Print ( "" );
//--- close all
   DatabaseFinalize(request);
   DatabaseClose(db);
   return ( true );
  }

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart ()
  {
   TestDB( "test.sqlite" ,DATABASE_OPEN_READWRITE | DATABASE_OPEN_CREATE |DATABASE_OPEN_COMMON);
  }


Output:
0 :   1 Paul 32 California 20000.0
1 :   3 Teddy 23 Norway 20000.0
2 :   4 Mark 25 Rich-Mond   65000.0



파일:
SqLiteTest.zip  2709 kb
 
멋진 새로운 소개 Renat! 그런 질문이 있었습니다.
ME 프로젝트 구조에 .sqlite 파일을 포함할 수 있습니까? .ex5의 후속 패키징을 위해
그렇다면 .sqlite 파일의 크기가 커질 때 .ex5 프로그램은 어떻게 작동합니까? 이미 컴파일된 .ex5 프로그램에서
 

새로운 기능에 감사드립니다.
나는 새로운 기능에 대한 좋은 참조를 개발 성공의 열쇠로 생각합니다. 도움말 자체에서 작업하는 방법에 대한 예제가 충분하지 않습니다.
또한 저희가 발견한 다음과 같은 결점에 주의를 기울일 것을 요청합니다.


1) DatabaseExecute 함수에 대한 설명은 사실이 아니지만 DatabasePrepare 에서 복사했습니다.

2) DatabaseRead 함수의 첫 번째 매개변수에 대한 불완전한 설명: int database , // DatabaseOpen에서 받은 데이터베이스 핸들;
DatabasePrepare 는 보다 완전한 정보를 제공하기 때문에 c 는 요청 핸들을 생성하고 DatabaseRead()로 실행할 수 있습니다 .

3) DatabaseTransactionXXX 함수는 실제로 위의 GetLastError() 오류 목록을 생성합니까, 아니면 "이전 실패의 후속 오류"를 수행합니까?

4) DatabaseTransactionХХХ 함수에 대해 중첩 트랜잭션 작업에 대한 정보가 제공되지 않습니다.

5) DatabaseColumnName 함수 매개변수 설명의 오타("필드 이름 가져오기"여야 함)
string& name // 테이블 이름 을 얻기 위한 변수 참조

 
Roman :
멋진 새로운 소개 Renat! 그런 질문이 있었습니다.
ME 프로젝트 구조에 .sqlite 파일을 포함할 수 있습니까? .ex5의 후속 패키징을 위해
그렇다면 .sqlite 파일의 크기가 커질 때 .ex5 프로그램은 어떻게 작동합니까? 이미 컴파일된 .ex5 프로그램에서

대부분의 경우 리소스에 포함되도록 허용하고 프로그램이 처음 실행될 때 이러한 파일이 자동으로 디스크에 추출됩니다.

즉, ex5 내부의 베이스가 부풀어 오르지 않습니다. 파일은 디스크에서만 조작할 수 있습니다.

 
Renat Fatkhullin :

데이터베이스는 DATABASE_OPEN_MEMORY 플래그를 사용하여 디스크와 메모리 에만 보관할 수 있습니다.

이것이 MT5 터미널 (SSD 파일을 죽이는 대신)과 터미널 내부의 프로그램(리소스 대신) 간에 데이터를 교환하기 위한 공식 메커니즘이라는 것을 올바르게 이해하고 있습니까?

 
Sergey Dzyublik :

새로운 기능에 감사드립니다.
나는 새로운 기능에 대한 좋은 참조를 개발 성공의 열쇠로 생각합니다. 도움말 자체에서 작업하는 방법에 대한 예제가 충분하지 않습니다.
또한 저희가 발견한 다음과 같은 결점에 주의를 기울일 것을 요청합니다.


1) DatabaseExecute 함수에 대한 설명은 사실이 아니지만 DatabasePrepare 에서 복사했습니다.

2) DatabaseRead 함수의 첫 번째 매개변수에 대한 불완전한 설명: int database , // DatabaseOpen에서 받은 데이터베이스 핸들;
DatabasePrepare 는 보다 완전한 정보를 제공하기 때문에 c 는 요청 핸들을 생성하고 DatabaseRead()로 실행할 수 있습니다 .

3) DatabaseTransactionXXX 함수는 실제로 위의 GetLastError() 오류 목록을 생성합니까, 아니면 "이전 실패의 후속 오류"를 수행합니까?

4) DatabaseTransactionХХХ 함수에 대해 중첩 트랜잭션 작업에 대한 정보가 제공되지 않습니다.

5) DatabaseColumnName 함수 매개변수 설명의 오타("필드 이름 가져오기"여야 함)
string& name // 테이블 이름 을 얻기 위한 변수 참조

도움말이 부분적으로 업데이트되었습니다. 다시 확인하십시오. 이것은 도움말의 첫 번째 버전이며 보완될 것입니다.

SQLite에는 중첩 트랜잭션이 없으므로 시도하지 마십시오.

주제에 예제가 나와 있으며 복잡한 것은 전혀 없습니다. 나중에 우리는 표준 라이브러리에서 기사와 래퍼 클래스를 만들 것입니다.

 
fxsaber :

이것이 MT5 터미널(SSD 파일을 죽이는 대신)과 터미널 내부의 프로그램(리소스 대신) 간에 데이터를 교환하기 위한 공식 메커니즘이라는 것을 올바르게 이해하고 있습니까?

편협한 사용자가 배포하는 "SSD 죽이기"에 대한 솔직한 말도 충분합니다.

아니오, 이것은 파일 기반입니다. 교환할 수 있지만 데이터베이스가 동시에 열려 있을 때 잠재적으로 독점적인 액세스로 인해 다른 Expert Advisors에서 동시에 액세스하는 것은 위험합니다.

" DATABASE_OPEN_MEMORY 플래그에 의해 메모리에서만" 열린 데이터베이스는 특정 프로그램에서만 사용할 수 있으며 누구와도 공유되지 않습니다.


데이터베이스의 응용 분야:

  1. MQL5 프로그램의 설정 및 상태 저장

  2. 대용량 데이터 저장

  3. 측면에서 준비한 데이터 사용

    예를 들어 Metatrader에서 SQLite로 데이터를 내보내고 기성품 수학 패키지를 사용하여 Python에서 이 데이터를 계산하고 결과도 SQlite 형식으로 넣었습니다.

다음 릴리스에는 편집기에서 직접 SQLite 데이터베이스 를 보고 편집할 수 있는 기본 지원이 포함되어 이러한 데이터베이스를 일반 데이터 교환 메커니즘으로 사용할 수 있습니다.
 
Renat Fatkhullin :

다음 릴리스에는 편집기에서 직접 SQLite 데이터베이스를 보고 편집할 수 있는 기본 지원이 포함되어 이러한 데이터베이스를 일반 데이터 교환 메커니즘으로 사용할 수 있습니다.

ME의 데이터베이스 편집기는 매우 편리할 것입니다. 감사합니다.

 
Renat Fatkhullin :

아니오, 이것은 파일 기반입니다. 교환할 수 있지만 데이터베이스가 동시에 열려 있을 때 잠재적으로 독점적인 액세스로 인해 다른 Expert Advisors에서 동시에 액세스하는 것은 위험합니다.

Python에는 다중 스레드 데이터베이스로 작업할 때 스레드로부터 안전한 I/O를 위한 Sqlite3Worker 라이브러리가 있습니다.
여러 전문가의 데이터베이스와 비동기식으로 작업할 수 있도록 구현을 mql로 이식하는 것에 대해 생각하는 것이 합리적일 수 있습니다.
글쎄, 또는 아이디어를 차용하고 자신의 비동기 입출력을 구현하십시오.

sqlite3worker
sqlite3worker
  • 2017.03.21
  • pypi.org
('Thread safe sqlite3 interface',)
 
Roman :

Python에는 다중 스레드 데이터베이스로 작업할 때 스레드로부터 안전한 I/O를 위한 Sqlite3Worker 라이브러리가 있습니다.
여러 전문가의 데이터베이스와 비동기식으로 작업할 수 있도록 구현을 mql로 이식하는 것에 대해 생각하는 것이 합리적일 수 있습니다.
글쎄, 또는 아이디어를 차용하고 자신의 비동기 입출력을 구현하십시오.

위의 성능 차트를 보셨습니까? MQL5에서는 종종 C++보다 더 빠르게 작동합니다.

물론 우리는 모든 것이 다중 스레드이며 모든 것이 정확합니다.

그것은 완전히 다른 것에 관한 것입니다. 다른 프로그램/프로세스 가 동일한 데이터베이스 파일에 독립적으로 올라가면 어떻게 됩니까? 하나의 프로그램(MQL5)이 아니라 서로에 대해 알지 못하고 동일한 데이터베이스 핸들을 사용하지 않는 여러 독립 프로그램입니다.

 
그리고 다른 프로그램/터미널에서 하나의 데이터베이스에 대한 액세스 동기화를 구성하는 데 근본적인 어려움이 있습니까? 또 에르사츠야?