Статьи

Загрузка 8 миллионов строк за 88 секунд — скорость NewSQL

Содержание этой статьи изначально было написано Пас Апичеллой.

В какой-то момент любой проект модернизации данных потребует загрузки устаревших данных. Располагая распределенным хранилищем данных в памяти, таким как SQLFire, клиенты часто спрашивают (как в этом случае) о времени загрузки, потому что они могут сидеть на 50-100 ГБ данных и не хотят ждать несколько дней. Для тех, кто не знаком с базами данных NewSQL, этот пост должен дать вам хорошее представление о том, как мы загрузили 8 миллионов строк за 88 секунд. Тест показывает, как мы сможем загрузить примерно 40 ГБ данных примерно за 1 час.

Для разработчиков Java, которым нужны идеи по ускорению больших объемов вычислений, преобразований или проверок, вы можете рассмотреть  предыдущую публикацию , где SQLFire используется с Spring Batch.

В SQLFire мы используем многопоточный подход к загрузке из файла CSV. Ниже я изложил 8 шагов к стратегии загрузки с объяснением того, почему что-то было сделано.

Пожалуйста, обратите внимание:

  • Это основано на SQLFire 1.0.3.
  • В этой конфигурации у нас есть 2 виртуальные машины, каждая с 18 ГБ памяти, которые совместно используют один и тот же домашний каталог, а также имеют отдельные диски для своих собственных дисковых хранилищ / файлов журналов.

1. Настройте SQLFire на своем пути, как показано ниже.

http://arunma.com/2011/11/prototype-design-pattern-mindmap-and-implementation/

2. На VM1 запустите локатор, как показано ниже

sqlf locator start -J-javaagent:/export/w2-gst-cert-20a/users/swale/sqlfire/build-
artifacts/linux/product- sqlf/lib/sqlfire.jar -peer-discovery-address=w2-gst-cert-21 -peer-discovery-
port=41111 -client-bind- address=w2-gst-cert-21 -client-port=1527 -dir=/w2-gst-cert-
21a/users/papicella/locator &

 

3. На VM1 и VM2 запустите 2 узла сервера SQLFire, как показано ниже.
Эти серверы имеют 18G памяти. VM2 — то, где процесс локатора был начат. Вывод ниже просто показывает, какие скрипты вам нужны для запуска этих серверов.

[Thu Jul 12 21:24:47 papicella@:~/pas/demo/load-demo ] $ cat start-server-w2-gst-cert-20.sh sqlf
server start -J-javaagent:/export/w2-gst-cert-20a/users/swale/sqlfire/build-artifacts/linux/product-
sqlf/lib/sqlfire.jar -server-groups=MYGROUP -initial-heap=8024m -max-heap=8024m -locators=w2- gst-cert-
21[41111] -client-bind-address=w2-gst-cert-20 -client-port=1527 -dir=/w2-gst-cert-20a/users/
papicella/server1 &

[Thu Jul 12 21:27:19 papicella@:~/pas/demo/load-demo ] $ cat start-server-w2-gst-cert-21.sh sqlf server start -J-javaagent:/export/w2-gst-cert-20a/users/swale/sqlfire/build-artifacts/linux/product- sqlf/lib/sqlfire.jar -server-groups=MYGROUP -initial-heap=8024m -max-heap=8024m -locators=w2- gst-cert-21[41111] -client-bind-address=w2-gst-cert-21 -client-port=1528 -dir=/w2-gst-cert-21a/users/ papicella/server2 &

4. Создайте хранилище дисков, используя SQL, как показано ниже.

CREATE DISKSTORE STORE1 WRITEBUFFERSIZE 19292393;

5. Установите глобальную политику выселения для таблиц в группе «MYGROUP».

call sys.set_eviction_heap_percentage_sg (85, 'MYGROUP');

6. Создайте таблицу, как показано ниже.
Мы используем отдельное хранилище на диске для сохранения, и любые данные переполнения будут просто удалены из памяти и не будут записаны на диск, так как сохранение включено здесь. Эта таблица будет асинхронно записывать свои изменения в хранилище дисков, так что ей нужно только выполнить вставку в память, прежде чем перейти к следующему изменению.

drop table emp;

CREATE TABLE emp
(empno integer NOT NULL primary key,
ename varchar(20),
hiredate date,
deptno int
)
SERVER GROUPS (MYGROUP)
EVICTION BY LRUHEAPPERCENT EVICTACTION OVERFLOW
PARTITION BY COLUMN (empno)
REDUNDANCY 1
PERSISTENT 'STORE1' ASYNCHRONOUS;

7. Предварительно создайте блоки, готовые для загрузки данных, запустив SQL, как показано ниже.
Если этого не сделать, то это будет сделано в первый раз, когда происходит вставка, когда SQLFire автоматически не создает сегменты.

call sys.create_all_buckets('APP.EMP');

Примечание. Это можно сделать после загрузки, как показано ниже.

call sys.rebalance_all_buckets()

8. Запустите сценарий для загрузки данных, показывающих, что вывод данных занял 88 секунд для загрузки 8 миллионов строк в кластер из 2 узлов.
В этом примере мы загружаем CSV-файл с именем «EMP.dat», который содержит 8 миллионов строк с данными, как показано ниже, и размер диска 371M.

Пример:

1,LkrpCkpGQpsUvZrFWAic,2012-02-29 05:35:10,2288856
2,gfbuiiWcUxoDOgeHuth,2012-01-10 12:57:18,1243600
3,GnR,2012-03-04 20:47:06,1533872
4,uxaGakuW,2012-03-17 20:57:34,2451659
5,coIrAvnPtvOZJirnea,2012-01-29 15:36:20,3731769
6,FJvnIjy,2012-03-24 07:37:59,4137455
7,rjnWsfbLldRtUSBDdZew,2012-01-24 13:09:22,3416189
8,DpbvPBkalbuZbBItgoJp,2012-03-28 13:41:56,6497992
9,jaba,2012-01-30 00:56:13,151525
10,EkDdiPeHBn,2012-04-02 12:05:25,630473
.....

 

«EMP.dat» существует только на VM2, поэтому нам нужно убедиться, что мы подключаемся только к этому узлу сервера SQLFire, и убедиться, что он единственный, ответственный за нагрузку, поскольку EMP.dat существует только на этом сервере. Для этого мы используем load-balance = false и используем read-timeout = 0, чтобы гарантировать, что клиент не остановится, если загрузка займет больше 5 минут.

скрипт loadEmp.sh

sqlf <<!
connect client 'w2-gst-cert-21:1528;load-balance=false;read-timeout=0';
ELAPSEDTIME on;
call syscs_util.import_table_ex('APP' /* schema */,
'EMP' /* table */,
'/w2-gst-cert-21a/users/papicella/data/EMP.dat' /* file path as seen by server */,
',' /* field separator */,
NULL,
NULL,
0,
0 /* don't lock the table */,
6 /* number of threads */,
0,
NULL /* custom class for data transformation or NULL to use the default inbuilt Import class */,
NULL);
!

 

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

[Thu Jul 12 19:03:23 papicella@:/w2-gst-cert-21a/users/papicella/data ] $ ./loadEMP.sh
sqlf version 10.4
sqlf> sqlf> sqlf> > > > > > > > > > > Statement executed.
ELAPSED TIME = 88324 milliseconds
sqlf>
[Thu Jul 12 19:04:58 papicella@:/w2-gst-cert-21a/users/papicella/data ] $

 

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

sqlf> select sqlentity, memory, substr(id, 1, 50) "Id" FROM sys.memoryAnalytics -- SQLFIRE-PROPERTIES sizerHints=withMemoryFootPrint;
SQLENTITY
|MEMORY
|Id
------------------------------------------------------------------------------------------------------------------------------
APP.EMP (Entry Size, Value Size, Row Count) | 832000000,204019274,8000000 | w2-gst-cert-21(10674)<v2>:2810/48871

APP.EMP (sqlfire,gemfire,others) | 241842808,229176368,196449984 (667469160 = 636.55 mb) | w2-gst-cert-21(10674)<v2>:2810/48871

APP.EMP (Entry Size, Value Size, Row Count) | 832000000,204019274,8000000 | w2-gst-cert-20(8550)<v1>:35007/36633

APP.EMP (sqlfire,gemfire,others) | 277524448,262689608,225528376 (765742432 = 730.27 mb) | w2-gst-cert-20(8550)<v1>:35007/36633

4 rows selected
ELAPSED TIME = 149673 milliseconds
sqlf>

Дополнительная информация

А. Ряд накладных

В настоящее время накладные расходы SQLFire для постоянной записи составляют около 100 байт, что увеличивает накладные расходы на 800 млн. Для 8 000 000 строк. Это необходимо учитывать при попытке определить объем памяти для таблиц при использовании постоянства.

B. Количество потоков для загрузки

При использовании syscs_util.import_table_ex лучше не использовать более 6 потоков для загрузки. Преимущество не увеличится, если вы добавите больше потоков, чем 6. В приведенном ниже примере просто используется 6 потоков для загрузки данных в SQLFire, что является наиболее эффективным способом.

sqlf < connect client 'w2-gst-cert-21:1528;load-balance=false;read-timeout=0';
ELAPSEDTIME on;
call syscs_util.import_table_ex('APP' /* schema */,
'EMP' /* table */,
'/w2-gst-cert-21a/users/papicella/data/EMP.dat' /* file path as seen by server */,
',' /* field separator */,
NULL,
NULL,
0,
0 /* don't lock the table */,
6 /* number of threads */,
0,
NULL /* custom class for data transformation or NULL to use the default inbuilt Import class */);
!

 

C. Подключение в качестве клиента, где существует файл данных

В приведенном ниже примере мы гарантируем, что длительно работающие клиенты нагрузки не истекают. По умолчанию время ожидания истекает через 5 минут, поэтому во избежание использования read-timeout = 0 для атрибута строки подключения. В то же время мы хотим выполнить загрузку только с одного клиента, где существует файл данных. В этом случае мы обеспечиваем подключение к клиенту, на котором существует файл данных, и отключаем балансировку нагрузки, чтобы другой узел также не пытался прочитать файл.

sqlf < connect client 'w2-gst-cert-21:1528;load-balance=false;read-timeout=0';
ELAPSEDTIME on;
call syscs_util.import_table_ex('APP' /* schema */,
'EMP' /* table */,
'/w2-gst-cert-21a/users/papicella/data/EMP.dat' /* file path as seen by server */,
',' /* field separator */,
NULL,
NULL,
0,
0 /* don't lock the table */,
6 /* number of threads */,
0,
NULL /* custom class for data transformation or NULL to use the default inbuilt Import class */);