На собеседованиях не всегда задают практические вопросы. Некоторые вопросы имеют цель показать общее знание кандидата в данной сфере. Это довольно спорный подход, но он встречается.
Здравствуйте. Слово "абстрактный" используется, чтобы подчеркнуть отличие от дефолтных методов, которых в функциональных интерфейсах может быть несколько.
Каждый выбор уникален и индивидуален, поэтому важно на этом этапе хорошо себя знать, а именно – свои сильные стороны и ценности. Если сейчас лично вам работа приносит удовольствие и вы в ней эффективны, и по вашей оценке ничего менять не нужно – это здорово. Но, если это не так, тогда стоит начать работу по выстраиванию карьерного будущего:
Осознать – какие дополнительные возможности вы бы приобрели, если бы приняли предложение руководителя;
Какие существуют способы для решения той творческой задачи, о который вы пишите;
Почему поступило данное предложение и какие сильные стороны увидел в вас руководитель для выполнения управленческих задач.
Порой нам бывает сложно осознать сильные стороны и даже принять их в себе, так как чаще всего нам кажется, что это есть у всех, но это не так :)
Если сейчас вы удовлетворены работой, то это отличное понимание себя!
Благодарим за мнение. Не будем забывать, что любой граф можно попробовать разложить в табличный вид с помощью матрицы N*N, где N - количество вершин, а связи указаны на пересечении строк и столбцов. Указанный пример не случайно легко представляется в табличном формате - большинство разработчиков баз данных сейчас работают в основном с SQL и таблицами. В статье как раз красной нитью прошита аналогия именно с табличным представлением, и очень хорошо, что вы её заметили. По мнению автора, для разработчиков SQL на прикладном примере из привычного мира реляционных СУБД подобные аналогии упрощают понимание графовых баз данных. Сложные примеры, направленные именно на демонстрацию графового представления планируется раскрыть в последующих статьях, но без раскрытия основ это будет тяжело разъяснить тем, кто привык общаться с базой с помощью SQL. Хотелось бы быть последовательными в публикациях.
Добрый день. Согласны с Вами, инструмент устарел и больше не поддерживается. Но также отметим, что во многих компаниях (в частности, в банках) разработка до сих пор ведётся на устаревающем/устаревшем наборе инструментов. Давайте взглянем на статью с другой стороны. Предположим, вы попали на проект, одной из задач которого является миграция данных из РСУБД в Hadoop. Из доступных инструментов у вас есть только Apache Sqoop (возможно Spark установлен, но не работает). Экспертизы по работе с Apache Sqoop у вас нет, но вам нужно понять, в случае подобных ограничений в использовании ETL инструментов, можно ли решить поставленную задачу с помощью Sqoop. На наш взгляд, ознакомившись с данным материалом, у Вас должно сложиться понимание о том, как решать эту задачу - остановиться на использовании Apache Sqoop или предложить заказчику использование другого более современного и гибкого инструмента для миграции данных.
Добрый день. Увы, "чистая" Кафка не предназначена для сложных манипуляций с данными.
Это как раз про "переложить из одного места в другое", по этой причине ее и относят к брокерам сообщений. [https://ru.wikipedia.org/wiki/Apache_Kafka]. А вот библиотеки и продукты выросшие на основе Кафки действительно способны на сложные манипуляции.
Действительно результатом агрегации (см. пример) будет не одна строка, а несколько. Причина такого поведения состоит в том, что в каждом временном диапазоне свой результат агрегации
SELECT max(age) as max_age
FROM str_persons0
WINDOW SESSION (5 MINUTES)
GROUP BY 1
EMIT CHANGES
;
Если вы хотите получить максимум только за последний временной промежуток, то можно либо применить дополнительный фильтр WHERE rowtime BETWEEN ... либо перенаправить результат стрима в новый топик (см. ниже) по которому уже можно будет сделать соответствующую выборку.
CREATE OR REPLACE TABLE strmax_persons
AS SELECT 1 as id
, max(age) as max_age
, max(rowtime) AS rt_dt
FROM str_persons
WINDOW TUMBLING (SIZE 5 MINUTES)
GROUP BY 1
EMIT CHANGES
;
CREATE OR REPLACE STREAM str_persons1 (
id BIGINT
, max_age INT
, rt_dt BIGINT
) WITH (
KAFKA_TOPIC = 'STRMAX_PERSONS'
, VALUE_FORMAT = 'JSON'
);
SELECT LATEST_BY_OFFSET(max_age)
FROM str_persons1
GROUP BY 1 EMIT CHANGES
LIMIT 1
;
Старались максимально упростить пример, но все равно он вышел достаточно объемным. Пример сделан исключительно для работы с Natasha и в нём не учитываются словари аббревиатур/сокращений и другая дополнительная обработка. Также для простоты приводим пример с сокращением текста на основе выбора предложений, в которых упоминается интересующая нас сущность.
from natasha import (
Segmenter,
MorphVocab,
NewsEmbedding,
NewsMorphTagger,
NewsSyntaxParser,
NewsNERTagger,
PER,
NamesExtractor,
Doc
)
segmenter = Segmenter()
emb = NewsEmbedding()
morph_tagger = NewsMorphTagger(emb)
syntax_parser = NewsSyntaxParser(emb)
morph_vocab = MorphVocab()
# ----------------------------- key sentences block -----------------------------
def find_synax_tokens_with_order(doc, start, tokens, text_arr, full_str):
''' Находит все синтаксические токены, соответствующие заданному набору простых токенов (найденные
для определенной NER другими функциями).
Возвращает словарь найденных синтаксических токенов (ключ - идентификатор токена, состоящий
из номера предложения и номера токена внутри предложения).
Начинает поиск с указанной позиции в списке синтаксических токенов, дополнительно возвращает
позицию остановки, с которой нужно продолжить поиск следующей NER.
'''
found = []
in_str = False
str_candidate = ''
str_counter = 0
if len(text_arr) == 0:
return [], start
for i in range(start, len(doc.syntax.tokens)):
t = doc.syntax.tokens[i]
if in_str:
str_counter += 1
if str_counter < len(text_arr) and t.text == text_arr[str_counter]:
str_candidate += t.text
found.append(t)
if str_candidate == full_str:
return found, i+1
else:
in_str = False
str_candidate = ''
str_counter = 0
found = []
if t.text == text_arr[0]:
found.append(t)
str_candidate = t.text
if str_candidate == full_str:
return found, i+1
in_str = True
return [], len(doc.syntax.tokens)
def find_tokens_in_diap_with_order(doc, start_token, diap):
''' Находит все простые токены (без синтаксической информации), которые попадают в
указанный диапазон. Эти диапазоны мы получаем из разметки NER.
Возвращает набор найденных токенов и в виде массива токенов, и в виде массива строчек.
Начинает поиск с указанной позиции в строке и дополнительно возвращает позицию остановки.
'''
found_tokens = []
found_text = []
full_str = ''
next_i = 0
for i in range(start_token, len(doc.tokens)):
t = doc.tokens[i]
if t.start > diap[-1]:
next_i = i
break
if t.start in diap:
found_tokens.append(t)
found_text.append(t.text)
full_str += t.text
return found_tokens, found_text, full_str, next_i
def add_found_arr_to_dict(found, dict_dest):
for synt in found:
dict_dest.update({synt.id: synt})
return dict_dest
def make_all_syntax_dict(doc):
all_syntax = {}
for synt in doc.syntax.tokens:
all_syntax.update({synt.id: synt})
return all_syntax
def is_consiquent(id_1, id_2):
''' Проверяет идут ли токены друг за другом без промежутка по ключам. '''
id_1_list = id_1.split('_')
id_2_list = id_2.split('_')
if id_1_list[0] != id_2_list[0]:
return False
return int(id_1_list[1]) + 1 == int(id_2_list[1])
def replace_found_to(found, x_str):
''' Заменяет последовательность токенов NER на «заглушку». '''
prev_id = '0_0'
for synt in found:
if is_consiquent(prev_id, synt.id):
synt.text = ''
else:
synt.text = x_str
prev_id = synt.id
def analyze_doc(text):
''' Запускает Natasha для анализа документа. '''
doc = Doc(text)
doc.segment(segmenter)
doc.tag_morph(morph_tagger)
doc.parse_syntax(syntax_parser)
ner_tagger = NewsNERTagger(emb)
doc.tag_ner(ner_tagger)
return doc
def find_non_sym_syntax_short(entity_name, doc, add_X=False, x_str='X'):
''' Отыскивает заданную сущность в тексте, среди всех NER (возможно, в другой грамматической форме).
entity_name - сущность, которую ищем;
doc - документ, в котором сделан препроцессинг Natasha;
add_X - сделать ли замену сущности на «заглушку»;
x_str - текст замены.
Возвращает:
all_found_syntax - словарь всех подходящих токенов образующих искомые сущности, в котором
в случае надобности произведена замена NER на «заглушку»;
all_syntax - словарь всех токенов.
'''
all_found_syntax = {}
current_synt_number = 0
current_tok_number = 0
# идем по всем найденным NER
for span in doc.spans:
span.normalize(morph_vocab)
if span.type != 'ORG':
continue
diap = range(span.start, span.stop)
# создаем словарь всех синтаксических элементов (ключ -- id из номера предложения и номера внутри предложения)
all_syntax = make_all_syntax_dict(doc)
# находим все простые токены внутри NER
found_tokens, found_text, full_str, current_tok_number = find_tokens_in_diap_with_order(doc, current_tok_number,
diap)
# по найденным простым токенам находим все синтаксические токены внутри данного NER
found, current_synt_number = find_synax_tokens_with_order(doc, current_synt_number, found_tokens, found_text,
full_str)
# если текст NER совпадает с указанной сущностью, то делаем замену
if entity_name.find(span.normal) >= 0 or span.normal.find(entity_name) >= 0:
if add_X:
replace_found_to(found, x_str)
all_found_syntax = add_found_arr_to_dict(found, all_found_syntax)
return all_found_syntax, all_syntax
def key_sentences(all_found_syntax):
''' Находит номера предложений с искомой NER. '''
key_sent_numb = {}
for synt in all_found_syntax.keys():
key_sent_numb.update({synt.split('_')[0]: 1})
return key_sent_numb
def openinig_punct(x):
opennings = ['«', '(']
return x in opennings
def key_sentences_str(entitiy_name, doc, add_X=False, x_str='X', return_all=True):
''' Составляет окончательный текст, в котором есть только предложения, где есть ключевая сущность,
эта сущность, если указано, заменяется на «заглушку».
'''
all_found_syntax, all_syntax = find_non_sym_syntax_short(entitiy_name, doc, add_X, x_str)
key_sent_numb = key_sentences(all_found_syntax)
str_ret = ''
for s in all_syntax.keys():
if (s.split('_')[0] in key_sent_numb.keys()) or (return_all):
to_add = all_syntax[s]
if s in all_found_syntax.keys():
to_add = all_found_syntax[s]
else:
if to_add.rel == 'punct' and not openinig_punct(to_add.text):
str_ret = str_ret.rstrip()
str_ret += to_add.text
if (not openinig_punct(to_add.text)) and (to_add.text != ''):
str_ret += ' '
return str_ret
# ----------------------------- key entities block -----------------------------
def find_synt(doc, synt_id):
for synt in doc.syntax.tokens:
if synt.id == synt_id:
return synt
return None
def is_subj(doc, synt, recursion_list=[]):
''' Сообщает является ли слово подлежащим или частью сложного подлежащего. '''
if synt.rel == 'nsubj':
return True
if synt.rel == 'appos':
found_head = find_synt(doc, synt.head_id)
if found_head.id in recursion_list:
return False
return is_subj(doc, found_head, recursion_list + [synt.id])
return False
def find_subjects_in_syntax(doc):
''' Выдает словарик, в котором для каждой NER написано, является ли он
подлежащим в предложении.
Выдает стартовую позицию NER и было ли оно подлежащим (или appos)
'''
found_subjects = {}
current_synt_number = 0
current_tok_number = 0
for span in doc.spans:
span.normalize(morph_vocab)
if span.type != 'ORG':
continue
found_subjects.update({span.start: 0})
diap = range(span.start, span.stop)
found_tokens, found_text, full_str, current_tok_number = find_tokens_in_diap_with_order(doc,
current_tok_number,
diap)
found, current_synt_number = find_synax_tokens_with_order(doc, current_synt_number, found_tokens,
found_text, full_str)
found_subjects.update({span.start: 0})
for synt in found:
if is_subj(doc, synt):
found_subjects.update({span.start: 1})
return found_subjects
def entity_weight(lst, c=1):
return c*lst[0]+lst[1]
def determine_subject(found_subjects, doc, new_agency_list, return_best=True, threshold=0.75):
''' Определяет ключевую NER и список самых важных NER, основываясь на том, сколько
раз каждая из них встречается в текста вообще и сколько раз в роли подлежащего '''
objects_arr = []
objects_arr_ners = []
should_continue = False
for span in doc.spans:
should_continue = False
span.normalize(morph_vocab)
if span.type != 'ORG':
continue
if span.normal in new_agency_list:
continue
for i in range(len(objects_arr)):
t, lst = objects_arr[i]
if t.find(span.normal) >= 0:
lst[0] += 1
lst[1] += found_subjects[span.start]
should_continue = True
break
if span.normal.find(t) >= 0:
objects_arr[i] = (span.normal, [lst[0]+1, lst[1]+found_subjects[span.start]])
should_continue = True
break
if should_continue:
continue
objects_arr.append((span.normal, [1, found_subjects[span.start]]))
objects_arr_ners.append(span.normal)
max_weight = 0
opt_ent = 0
for obj in objects_arr:
t, lst = obj
w = entity_weight(lst)
if max_weight < w:
max_weight = w
opt_ent = t
if not return_best:
return opt_ent, objects_arr_ners
bests = []
for obj in objects_arr:
t, lst = obj
w = entity_weight(lst)
if max_weight*threshold < w:
bests.append(t)
return opt_ent, bests
text = '''В офисах Сбера начали тестировать технологию помощи посетителям в экстренных ситуациях. «Зеленая кнопка» будет
в зонах круглосуточного обслуживания офисов банка в Воронеже, Санкт-Петербурге, Подольске, Пскове, Орле и Ярославле.
В них находятся стенды с сенсорными кнопками, обеспечивающие связь с операторами центра мониторинга службы безопасности
банка. Получив сигнал о помощи, оператор центра может подключиться к объекту по голосовой связи. С помощью камер
видеонаблюдения он оценит обстановку и при необходимости вызовет полицию или скорую помощь. «Зеленой кнопкой» можно
воспользоваться в нерабочее для отделения время, если возникла угроза жизни или здоровью. В остальных случаях помочь
клиентам готовы сотрудники отделения банка. «Одно из направлений нашей работы в области ESG и устойчивого развития
— это забота об обществе. И здоровье людей как высшая ценность является его основой. Поэтому задача банка в области
безопасности гораздо масштабнее, чем обеспечение только финансовой безопасности клиентов. Этот пилотный проект
приурочен к 180-летию Сбербанка: мы хотим, чтобы, приходя в банк, клиент чувствовал, что его жизнь и безопасность
— наша ценность», — отметил заместитель председателя правления Сбербанка Станислав Кузнецов.'''
doc = analyze_doc(text)
key_entity = determine_subject(find_subjects_in_syntax(doc), doc, [])[0]
res = key_sentences_str(key_entity, doc, add_X=True, x_str='X', return_all=False)
При этом в статье обсуждаются возможности Oracle APEX версии 5.1, вышедшего в 2017 году, при том что сейчас, в конце 2021 года, текущий Oracle APEX имеет версию 21.2 и в нем есть разные как положительные, так и не очень изменения по сравнению с версией 5.1.
Как часто бывает, с момента возникновения идеи статьи до её публикации прошло много времени. С версией 21 не работал, смотрел только на 19-ю, и не в контексте графиков.
Ну и Oracle BIEE 12 (с ним последний раз работал ещё раньше) упомянут в контексте, что он дорогой, профессиональный, а по факту - тоже не всемогущий :)
Пример из готового продукта выложить не сможем, также в препроцессинге довольно много строк кода. Поэтому подготовим простой пример с необходимой логикой и в ближайшие дни поделимся им.
Apache Sqoop также является одним из решений миграции данных между РСУБД и Hadoop, но при миграции данных, может возникнуть потребность в индивидуальной обработке при извлечении данных и загрузкой в Hadoop. В таком случае Sqoop плохо подходит для реализации такого рода задач, и тогда предпочтительней будет использовать утилиты JDBC Apache Spark.
Вы правы, отметив, что миграция не заканчивается на выполнении одного запроса, особенно, если бизнес хочет иметь в hadoop копию через час после закрытия опердня для построения отчетности. Для данных задач, может потребоваться разработка различных систем и сервисов, с развернутой архитектурой для реализации конкретных задач. В статье хотелось отразить концепцию взаимодействия СУБД и HADOOP, а также отразить некоторые важные нюансы протекания данного процесса.
Рассматривая вопрос скорости доставления данных по JDBC, тут есть множество посторонних факторов, которые могут повлиять на данный процесс (например: скорость передачи данных по сети, характеристики жестких дисков и др.). Рассматривая системы, с которыми мне приходилось работать, средняя скорость доставки данных для различных систем варьировалась от 70 до 100 Мбит/c.
Она и не нужна. Если подходить так, придется писать полноценный транслятор по каждому инструменту.
Но мы же решаем свою собственную задачу. Зачем нам полноценный транслятор?
Пример:
В проекте есть есть sqoop скрипты, которые выглядят так:
sqoop --query "select * from b1.t1 join b1.t2 on t1.id = t2.t1_id" --table b2.t3
То есть логика датафлоу запакована внутрь sqoop и там внутрь sql.
Но ведь для данного проекта, чтобы получить данные о датафлоу нужно всего лишь выполнить примерно такой условный скрипт:
val tables = sqoopScript.splitBy([' ', ',', '(', ')']).match('[a-z]/.[a-z]')
не уверен что синтаксис правильный, но суть в чем - разбить содержимое файла на токены и оставить из них только те, где токен это два слова разделенных символом точка. мы получим b1.t1, b2.t2, b2.t3
То есть мы проанализировали проект. Нашли в нем пачку скриптов, и для данного конкретного случая записали такое простое правила парсинга. Это правило для данного конкретного случая, а не общий парсер инструмента. В этом суть. И тогда это работает.
Все правильно. Те же самые опасения были и у нас. Вообще, всего в статье не опишешь, но проект начинался с немного другой архитектурой. Предполагалось, что мы для себя, для наших проектов, изобретем правила, когда логику будем выносить во внешний файл, где будут имена таблиц и логика обработки. Вот этот внешний файл и предполагалось парсить и отображать в виде диаграммы. Но постепенно выяснилось, что этого не требуется. Парсить с десяток языков для извлечения из них информации по датафлоу не так уж и страшно. Реальные трудозатраты на это вполне умеренные, особенно, по сравнению с трудозатратами на сам проект.
Да, все правильно. Именно об этом и статья, что проект обычно получается сложный, и что делать с ним не ясно. Но в реальности, как показывает практика, эта сложность разбивается на 3-5 вполне себе прозрачных варианта, что и позволяет извлечь информацию и построить dataflow проекта. Да, такие диаграммы не описывают всю сложность, как и data catalog, но то, что получается, это набор диаграмм из реальных артефактов проекта, и это намного лучше, чем ничего отсутствие документации вообще.
Что касается вариантов представления информации о таблицах, я сам их могу привести штук 20 не из головы, а из реальных проектов. И вот тут, как раз, и находится основание для такого проекта. Этих вариантов не 500 и не 5000, а намного меньше, то есть столько, что позволяет построить набор правил для парсинга исходников и извлечения из них информации о dataflow.
Как выглядит обычный проект развивавшийся года 3-4? Несколько сотен файлов исходников, несколько десятков таблиц. Что с этим делать? Да вот написать 5-6 правил, которые позволят построить диаграммы dataflow. Это и есть наш проект.
Да, схемы на выходе из одного и входе в другой датасет мы не проверяем, такая проблема есть. Но отсутствие проверки схем основано на том, что в spark вообще поддерживается 'select * from table1'. По крайней мере в нашем случае эта проблема не сильно портит картину. Как раз для валидации запросов и есть в нашем подходе отладка — возможность прогнать задачу до нужного датасета. Но вообще вопрос поставлен верно. Если на основе описанного нами подхода делать серьезный инструмент, то валидацию схем туда надо бы прикрутить. Но как раз необходимости серьезного инструмента мы и хотели избежать.
Согласны с вами, но иногда спрашивают и эти моменты.
На собеседованиях не всегда задают практические вопросы. Некоторые вопросы имеют цель показать общее знание кандидата в данной сфере. Это довольно спорный подход, но он встречается.
Здравствуйте. Слово "абстрактный" используется, чтобы подчеркнуть отличие от дефолтных методов, которых в функциональных интерфейсах может быть несколько.
Это в формате юмора, но спасибо за комментарий, учтем в дальнейшем.
Каждый выбор уникален и индивидуален, поэтому важно на этом этапе хорошо себя знать, а именно – свои сильные стороны и ценности. Если сейчас лично вам работа приносит удовольствие и вы в ней эффективны, и по вашей оценке ничего менять не нужно – это здорово. Но, если это не так, тогда стоит начать работу по выстраиванию карьерного будущего:
Осознать – какие дополнительные возможности вы бы приобрели, если бы приняли предложение руководителя;
Какие существуют способы для решения той творческой задачи, о который вы пишите;
Почему поступило данное предложение и какие сильные стороны увидел в вас руководитель для выполнения управленческих задач.
Порой нам бывает сложно осознать сильные стороны и даже принять их в себе, так как чаще всего нам кажется, что это есть у всех, но это не так :)
Если сейчас вы удовлетворены работой, то это отличное понимание себя!
Благодарим за мнение. Не будем забывать, что любой граф можно попробовать разложить в табличный вид с помощью матрицы N*N, где N - количество вершин, а связи указаны на пересечении строк и столбцов. Указанный пример не случайно легко представляется в табличном формате - большинство разработчиков баз данных сейчас работают в основном с SQL и таблицами. В статье как раз красной нитью прошита аналогия именно с табличным представлением, и очень хорошо, что вы её заметили. По мнению автора, для разработчиков SQL на прикладном примере из привычного мира реляционных СУБД подобные аналогии упрощают понимание графовых баз данных. Сложные примеры, направленные именно на демонстрацию графового представления планируется раскрыть в последующих статьях, но без раскрытия основ это будет тяжело разъяснить тем, кто привык общаться с базой с помощью SQL. Хотелось бы быть последовательными в публикациях.
Добрый день. Согласны с Вами, инструмент устарел и больше не поддерживается. Но также отметим, что во многих компаниях (в частности, в банках) разработка до сих пор ведётся на устаревающем/устаревшем наборе инструментов. Давайте взглянем на статью с другой стороны. Предположим, вы попали на проект, одной из задач которого является миграция данных из РСУБД в Hadoop. Из доступных инструментов у вас есть только Apache Sqoop (возможно Spark установлен, но не работает). Экспертизы по работе с Apache Sqoop у вас нет, но вам нужно понять, в случае подобных ограничений в использовании ETL инструментов, можно ли решить поставленную задачу с помощью Sqoop. На наш взгляд, ознакомившись с данным материалом, у Вас должно сложиться понимание о том, как решать эту задачу - остановиться на использовании Apache Sqoop или предложить заказчику использование другого более современного и гибкого инструмента для миграции данных.
Добрый день. Увы, "чистая" Кафка не предназначена для сложных манипуляций с данными.
Это как раз про "переложить из одного места в другое", по этой причине ее и относят к брокерам сообщений. [https://ru.wikipedia.org/wiki/Apache_Kafka]. А вот библиотеки и продукты выросшие на основе Кафки действительно способны на сложные манипуляции.
Добрый день.
Действительно результатом агрегации (см. пример) будет не одна строка, а несколько. Причина такого поведения состоит в том, что в каждом временном диапазоне свой результат агрегации
Если вы хотите получить максимум только за последний временной промежуток, то можно либо применить дополнительный фильтр WHERE rowtime BETWEEN ... либо перенаправить результат стрима в новый топик (см. ниже) по которому уже можно будет сделать соответствующую выборку.
Старались максимально упростить пример, но все равно он вышел достаточно объемным. Пример сделан исключительно для работы с Natasha и в нём не учитываются словари аббревиатур/сокращений и другая дополнительная обработка. Также для простоты приводим пример с сокращением текста на основе выбора предложений, в которых упоминается интересующая нас сущность.
Как часто бывает, с момента возникновения идеи статьи до её публикации прошло много времени. С версией 21 не работал, смотрел только на 19-ю, и не в контексте графиков.
Ну и Oracle BIEE 12 (с ним последний раз работал ещё раньше) упомянут в контексте, что он дорогой, профессиональный, а по факту - тоже не всемогущий :)
Спасибо за обратную связь!
Пример из готового продукта выложить не сможем, также в препроцессинге довольно много строк кода. Поэтому подготовим простой пример с необходимой логикой и в ближайшие дни поделимся им.
Apache Sqoop также является одним из решений миграции данных между РСУБД и Hadoop, но при миграции данных, может возникнуть потребность в индивидуальной обработке при извлечении данных и загрузкой в Hadoop. В таком случае Sqoop плохо подходит для реализации такого рода задач, и тогда предпочтительней будет использовать утилиты JDBC Apache Spark.
Вы правы, отметив, что миграция не заканчивается на выполнении одного запроса, особенно, если бизнес хочет иметь в hadoop копию через час после закрытия опердня для построения отчетности. Для данных задач, может потребоваться разработка различных систем и сервисов, с развернутой архитектурой для реализации конкретных задач. В статье хотелось отразить концепцию взаимодействия СУБД и HADOOP, а также отразить некоторые важные нюансы протекания данного процесса.
Рассматривая вопрос скорости доставления данных по JDBC, тут есть множество посторонних факторов, которые могут повлиять на данный процесс (например: скорость передачи данных по сети, характеристики жестких дисков и др.). Рассматривая системы, с которыми мне приходилось работать, средняя скорость доставки данных для различных систем варьировалась от 70 до 100 Мбит/c.
Она и не нужна. Если подходить так, придется писать полноценный транслятор по каждому инструменту.
Но мы же решаем свою собственную задачу. Зачем нам полноценный транслятор?
Пример:
В проекте есть есть sqoop скрипты, которые выглядят так:
sqoop --query "select * from b1.t1 join b1.t2 on t1.id = t2.t1_id" --table b2.t3
То есть логика датафлоу запакована внутрь sqoop и там внутрь sql.
Но ведь для данного проекта, чтобы получить данные о датафлоу нужно всего лишь выполнить примерно такой условный скрипт:
val tables = sqoopScript.splitBy([' ', ',', '(', ')']).match('[a-z]/.[a-z]')
не уверен что синтаксис правильный, но суть в чем - разбить содержимое файла на токены и оставить из них только те, где токен это два слова разделенных символом точка. мы получим b1.t1, b2.t2, b2.t3
То есть мы проанализировали проект. Нашли в нем пачку скриптов, и для данного конкретного случая записали такое простое правила парсинга. Это правило для данного конкретного случая, а не общий парсер инструмента. В этом суть. И тогда это работает.
Все правильно. Те же самые опасения были и у нас. Вообще, всего в статье не опишешь, но проект начинался с немного другой архитектурой. Предполагалось, что мы для себя, для наших проектов, изобретем правила, когда логику будем выносить во внешний файл, где будут имена таблиц и логика обработки. Вот этот внешний файл и предполагалось парсить и отображать в виде диаграммы. Но постепенно выяснилось, что этого не требуется. Парсить с десяток языков для извлечения из них информации по датафлоу не так уж и страшно. Реальные трудозатраты на это вполне умеренные, особенно, по сравнению с трудозатратами на сам проект.
Да, все правильно. Именно об этом и статья, что проект обычно получается сложный, и что делать с ним не ясно. Но в реальности, как показывает практика, эта сложность разбивается на 3-5 вполне себе прозрачных варианта, что и позволяет извлечь информацию и построить dataflow проекта. Да, такие диаграммы не описывают всю сложность, как и data catalog, но то, что получается, это набор диаграмм из реальных артефактов проекта, и это намного лучше, чем ничего отсутствие документации вообще.
Что касается вариантов представления информации о таблицах, я сам их могу привести штук 20 не из головы, а из реальных проектов.
И вот тут, как раз, и находится основание для такого проекта. Этих вариантов не 500 и не 5000, а намного меньше, то есть столько, что позволяет построить набор правил для парсинга исходников и извлечения из них информации о dataflow.
Как выглядит обычный проект развивавшийся года 3-4? Несколько сотен файлов исходников, несколько десятков таблиц. Что с этим делать? Да вот написать 5-6 правил, которые позволят построить диаграммы dataflow. Это и есть наш проект.
docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_accounts_remove.html +
docs.aws.amazon.com/awsaccountbilling/latest/aboutv2/close-account.html
Автоматизировать процесс подготовки аккаунта перед удалением пока не планируется.