Как обновить локальную таблицу удаленно?

#kdb

#kdb

Вопрос:

У меня есть большая таблица на удаленном сервере с неизвестным (миллионным) количеством строк данных. Я хотел бы иметь возможность извлекать данные партиями по 100 000 строк за раз, обновлять мою локальную таблицу этими извлеченными строками и выполнять это до тех пор, пока не будут извлечены все строки. Есть ли способ, которым я могу обновить локальную таблицу удаленно?

В настоящее время у меня есть фиктивная таблица, вызываемая t на сервере вместе со следующими переменными…

 t:([]sym:1000000?`A`B`Ab`Ba`C`D`Cd`Dc;id:1 til 1000000)
selector:select from t where sym like "A*"
counter:count selector
divy:counter0000
divyUP:ceiling divy
  

и приведенная ниже функция на клиенте вместе с переменными index , равными 0 и normTable , которая является копией удаленной таблицы…

 index:0
normTable:h"0#t"

batches:{[idx;divy;anty;seltr]
    if[not idx=divy;
    batch:select[(anty;100000)] from seltr;
    `normTable upsert batch;
    idx ::1;
    divy:divy;
    anty :100000;
    seltr:seltr;
    batches[idx;divy;anty;seltr]];
    idx::0}
  

Я вызываю эту функцию, используя следующую команду…

  batches[index;h"divyUP";0;h"selector"]
  

Проблема с этим подходом, однако, заключается h"selector" в том, что все строки данных извлекаются одновременно (и несколько раз — для каждого пакета из 100 000, который он отправляет на мой локальный normTable ).

Я мог бы переместить batches функцию на удаленный сервер, но тогда как мне обновить локальную normTable удаленно?

В качестве альтернативы я мог бы разбить строки на пакеты на сервере, а затем извлекать каждый пакет по отдельности. Но если я не знаю, сколько в ней строк, как мне узнать, сколько переменных требуется? Например, будет работать следующее, но только до первых 400 тыс. строк…

 batch1:select[100000] from t where symbol like "A*"
batch2:select[100000 100000] from t where symbol like "A*"
batch3:select[200000 100000] from t where symbol like "A*"
batch4:select[300000 100000] from t where symbol like "A*"
  

Есть ли способ установить batchX переменную так, чтобы она создавала новую переменную, равную количеству divyUP ?

Ответ №1:

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

  • Не запускайте синхронный запрос, так как это заставит сервер замедлить его обработку. Попробуйте выполнить асинхронный запрос с использованием обратных вызовов.
  • Не выполняйте полное сканирование таблицы (для тщательного сравнения) при каждом вызове специально для регулярных выражений. Возможно, что большая часть данных может быть доступна в кэше при следующем вызове, но все же это не гарантировано, что снова повлияет на нормальную работу сервера.
  • Не делайте запросы данных в пакетном режиме. Либо используйте таймер, либо выполните другой вызов запроса данных, когда поступят последние пакетные данные.

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

Создать функцию пакетной обработки

Эта функция будет выполняться на сервере и считывать небольшую партию данных из таблицы с использованием индексов и возвращать требуемые данные.

 q) batch:{[ind;s] ni:ind s; d:select from t where i within (ind;ni), sym like "A*"; 
                  neg[.z.w](`upd;d;$[ni<count t;ni 1;0]) }
  

Для работы требуется 2 аргумента — начальный индекс и размер пакета.

Эта функция, наконец, вызовет upd функцию на локальном компьютере mac асинхронно и передаст 2 аргумента.

  • Индекс таблицы для запуска следующего пакета (возвращает 0, если все строки выполнены, чтобы остановить следующую пакетную обработку)
  • Данные из текущего пакетного запроса

Создать функцию обратного вызова

Результат от функции пакетной обработки войдет в эту функцию.

Если индекс> 0, это означает, что требуется обработать больше данных, и следующий пакет должен начинаться с этого индекса.

 q) upd:{[data;ind] t::t,data;if[ind>0;fetch ind]}
  

Создайте основную функцию для запуска процесса

 q)fetch:{[ind] h (batch;ind;size)}
  

Наконец, откройте соединение, создайте табличную переменную и запустите fetch функцию.

 q) h:hopen `:server:port
q) t:()
q) size:100
q) fetch 0
  

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

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

Комментарии:

1. Спасибо, Рахул. Я не очень хорошо знаком с обратными вызовами в kdb (сейчас изучаю его). Работают ли функция обратного вызова и основная функция как на клиенте, так и на сервере или они только на клиенте или сервере?

2. Все функции, которые я упомянул, будут на клиенте. Обратные вызовы обычно представляют собой реализацию функции на стороне клиента, которую другой процесс вызывает после завершения запроса клиента в асинхронном вызове. Другой способ думать об этом — издатель, вызывающий функцию у подписчика всякий раз, когда происходит обновление.

Ответ №2:

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

 {[h;i]`mytab upsert h({select from t where i in x};i)}[h]each 0N 100000#til h"count t"
  

И вы можете легко изменить его, чтобы контролировать количество пакетов (а не размер), вместо этого используя 10 0N# (это сделало бы это за 10 пакетов)

Ответ №3:

Вместо того, чтобы использовать отдельные переменные, функция cut может разделить результат выбора на фрагменты по 100000 строк. Индексирование каждого элемента представляет собой таблицу.

 batches:100000 cut select from t where symbol like "A*"