This is an automated email from the ASF dual-hosted git repository. liyang pushed a commit to branch doc5.0 in repository https://gitbox.apache.org/repos/asf/kylin.git
commit 57201ac288ebeafeca75c06c74de37a8347108cd Author: jlf <[email protected]> AuthorDate: Tue Oct 22 16:10:24 2024 +0800 add i18n blog --- website/i18n/zh-Hans/code.json | 421 ++++++++++++++ .../2022-07-29-RoadMap_of_ApacheCon/img/1.png | Bin 0 -> 195659 bytes .../2022-07-29-RoadMap_of_ApacheCon/img/2.png | Bin 0 -> 187515 bytes .../2022-07-29-RoadMap_of_ApacheCon/img/3.png | Bin 0 -> 168408 bytes .../2022-07-29-RoadMap_of_ApacheCon/img/4.png | Bin 0 -> 1003125 bytes .../2022-07-29-RoadMap_of_ApacheCon/img/5.png | Bin 0 -> 72859 bytes .../2022-07-29-RoadMap_of_ApacheCon/img/6.png | Bin 0 -> 185016 bytes .../2022-07-29-RoadMap_of_ApacheCon/img/7.png | Bin 0 -> 179692 bytes .../2022-07-29-RoadMap_of_ApacheCon/img/8.png | Bin 0 -> 142855 bytes .../2022-07-29-RoadMap_of_ApacheCon/index.md | 160 +++++ .../2022-12-18-Introduction_of_Metadata/index.md | 322 +++++++++++ .../protocol-buffer/metadata.proto | 643 +++++++++++++++++++++ .../diagram/ER-diagram/metadata_store.png | Bin 0 -> 104881 bytes .../diagram/ER-diagram/metadata_store.puml | 44 ++ .../write_and_read_persistent_entity.puml | 98 ++++ .../write_and_read_persistent_entity_cn.png | Bin 0 -> 272752 bytes .../write_and_read_persistent_entity_cn.puml | 68 +++ .../class_diagram/metastore_resource_store.png | Bin 0 -> 175673 bytes .../class_diagram/metastore_resource_store.puml | 126 ++++ .../2023_01_16-Introduction_of_Metastore/index.md | 239 ++++++++ .../docusaurus-plugin-content-blog/authors.yml | 17 + .../docusaurus-plugin-content-blog/options.json | 14 + .../docusaurus-plugin-content-docs/current.json | 154 +++++ .../current/development/intro.md | 38 ++ .../zh-Hans/docusaurus-theme-classic/footer.json | 58 ++ .../zh-Hans/docusaurus-theme-classic/navbar.json | 30 + 26 files changed, 2432 insertions(+) diff --git a/website/i18n/zh-Hans/code.json b/website/i18n/zh-Hans/code.json new file mode 100644 index 0000000000..cc710d8af4 --- /dev/null +++ b/website/i18n/zh-Hans/code.json @@ -0,0 +1,421 @@ +{ + "theme.ErrorPageContent.title": { + "message": "页面已崩溃。", + "description": "The title of the fallback page when the page crashed" + }, + "theme.BackToTopButton.buttonAriaLabel": { + "message": "回到顶部", + "description": "The ARIA label for the back to top button" + }, + "theme.blog.archive.title": { + "message": "历史博文", + "description": "The page & hero title of the blog archive page" + }, + "theme.blog.archive.description": { + "message": "历史博文", + "description": "The page & hero description of the blog archive page" + }, + "theme.blog.paginator.navAriaLabel": { + "message": "博文列表分页导航", + "description": "The ARIA label for the blog pagination" + }, + "theme.blog.paginator.newerEntries": { + "message": "较新的博文", + "description": "The label used to navigate to the newer blog posts page (previous page)" + }, + "theme.blog.paginator.olderEntries": { + "message": "较旧的博文", + "description": "The label used to navigate to the older blog posts page (next page)" + }, + "theme.blog.post.paginator.navAriaLabel": { + "message": "博文分页导航", + "description": "The ARIA label for the blog posts pagination" + }, + "theme.blog.post.paginator.newerPost": { + "message": "较新一篇", + "description": "The blog post button label to navigate to the newer/previous post" + }, + "theme.blog.post.paginator.olderPost": { + "message": "较旧一篇", + "description": "The blog post button label to navigate to the older/next post" + }, + "theme.tags.tagsPageLink": { + "message": "查看所有标签", + "description": "The label of the link targeting the tag list page" + }, + "theme.colorToggle.ariaLabel": { + "message": "切换浅色/暗黑模式(当前为{mode})", + "description": "The ARIA label for the navbar color mode toggle" + }, + "theme.colorToggle.ariaLabel.mode.dark": { + "message": "暗黑模式", + "description": "The name for the dark color mode" + }, + "theme.colorToggle.ariaLabel.mode.light": { + "message": "浅色模式", + "description": "The name for the light color mode" + }, + "theme.docs.DocCard.categoryDescription.plurals": { + "message": "{count} 个项目", + "description": "The default description for a category card in the generated index about how many items this category includes" + }, + "theme.docs.breadcrumbs.navAriaLabel": { + "message": "页面路径", + "description": "The ARIA label for the breadcrumbs" + }, + "theme.docs.paginator.navAriaLabel": { + "message": "文件选项卡", + "description": "The ARIA label for the docs pagination" + }, + "theme.docs.paginator.previous": { + "message": "上一页", + "description": "The label used to navigate to the previous doc" + }, + "theme.docs.paginator.next": { + "message": "下一页", + "description": "The label used to navigate to the next doc" + }, + "theme.docs.tagDocListPageTitle.nDocsTagged": { + "message": "{count} 篇文档带有标签", + "description": "Pluralized label for \"{count} docs tagged\". Use as much plural forms (separated by \"|\") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)" + }, + "theme.docs.tagDocListPageTitle": { + "message": "{nDocsTagged}「{tagName}」", + "description": "The title of the page for a docs tag" + }, + "theme.docs.versionBadge.label": { + "message": "版本:{versionLabel}" + }, + "theme.docs.versions.unreleasedVersionLabel": { + "message": "此为 {siteTitle} {versionLabel} 版尚未发行的文档。", + "description": "The label used to tell the user that he's browsing an unreleased doc version" + }, + "theme.docs.versions.unmaintainedVersionLabel": { + "message": "此为 {siteTitle} {versionLabel} 版的文档,现已不再积极维护。", + "description": "The label used to tell the user that he's browsing an unmaintained doc version" + }, + "theme.docs.versions.latestVersionSuggestionLabel": { + "message": "最新的文档请参阅 {latestVersionLink} ({versionLabel})。", + "description": "The label used to tell the user to check the latest version" + }, + "theme.docs.versions.latestVersionLinkLabel": { + "message": "最新版本", + "description": "The label used for the latest version suggestion link label" + }, + "theme.common.editThisPage": { + "message": "编辑此页", + "description": "The link label to edit the current page" + }, + "theme.common.headingLinkTitle": { + "message": "{heading}的直接链接", + "description": "Title for link to heading" + }, + "theme.lastUpdated.atDate": { + "message": "于 {date} ", + "description": "The words used to describe on which date a page has been last updated" + }, + "theme.lastUpdated.byUser": { + "message": "由 {user} ", + "description": "The words used to describe by who the page has been last updated" + }, + "theme.lastUpdated.lastUpdatedAtBy": { + "message": "最后{byUser}{atDate}更新", + "description": "The sentence used to display when a page has been last updated, and by who" + }, + "theme.navbar.mobileVersionsDropdown.label": { + "message": "选择版本", + "description": "The label for the navbar versions dropdown on mobile view" + }, + "theme.NotFound.title": { + "message": "找不到页面", + "description": "The title of the 404 page" + }, + "theme.tags.tagsListLabel": { + "message": "标签:", + "description": "The label alongside a tag list" + }, + "theme.admonition.caution": { + "message": "警告", + "description": "The default label used for the Caution admonition (:::caution)" + }, + "theme.admonition.danger": { + "message": "危险", + "description": "The default label used for the Danger admonition (:::danger)" + }, + "theme.admonition.info": { + "message": "信息", + "description": "The default label used for the Info admonition (:::info)" + }, + "theme.admonition.note": { + "message": "备注", + "description": "The default label used for the Note admonition (:::note)" + }, + "theme.admonition.tip": { + "message": "提示", + "description": "The default label used for the Tip admonition (:::tip)" + }, + "theme.admonition.warning": { + "message": "注意", + "description": "The default label used for the Warning admonition (:::warning)" + }, + "theme.AnnouncementBar.closeButtonAriaLabel": { + "message": "关闭", + "description": "The ARIA label for close button of announcement bar" + }, + "theme.blog.sidebar.navAriaLabel": { + "message": "最近博文导航", + "description": "The ARIA label for recent posts in the blog sidebar" + }, + "theme.CodeBlock.copied": { + "message": "复制成功", + "description": "The copied button label on code blocks" + }, + "theme.CodeBlock.copyButtonAriaLabel": { + "message": "复制代码到剪贴板", + "description": "The ARIA label for copy code blocks button" + }, + "theme.CodeBlock.copy": { + "message": "复制", + "description": "The copy button label on code blocks" + }, + "theme.CodeBlock.wordWrapToggle": { + "message": "切换自动换行", + "description": "The title attribute for toggle word wrapping button of code block lines" + }, + "theme.DocSidebarItem.expandCategoryAriaLabel": { + "message": "展开侧边栏分类 '{label}'", + "description": "The ARIA label to expand the sidebar category" + }, + "theme.DocSidebarItem.collapseCategoryAriaLabel": { + "message": "折叠侧边栏分类 '{label}'", + "description": "The ARIA label to collapse the sidebar category" + }, + "theme.NavBar.navAriaLabel": { + "message": "主导航", + "description": "The ARIA label for the main navigation" + }, + "theme.navbar.mobileLanguageDropdown.label": { + "message": "选择语言", + "description": "The label for the mobile language switcher dropdown" + }, + "theme.TOCCollapsible.toggleButtonLabel": { + "message": "本页总览", + "description": "The label used by the button on the collapsible TOC component" + }, + "theme.NotFound.p1": { + "message": "我们找不到您要找的页面。", + "description": "The first paragraph of the 404 page" + }, + "theme.NotFound.p2": { + "message": "请联系原始链接来源网站的所有者,并告知他们链接已损坏。", + "description": "The 2nd paragraph of the 404 page" + }, + "theme.blog.post.readMore": { + "message": "阅读更多", + "description": "The label used in blog post item excerpts to link to full blog posts" + }, + "theme.blog.post.readMoreLabel": { + "message": "阅读 {title} 的全文", + "description": "The ARIA label for the link to full blog posts from excerpts" + }, + "theme.blog.post.readingTime.plurals": { + "message": "阅读需 {readingTime} 分钟", + "description": "Pluralized label for \"{readingTime} min read\". Use as much plural forms (separated by \"|\") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)" + }, + "theme.docs.breadcrumbs.home": { + "message": "主页面", + "description": "The ARIA label for the home page in the breadcrumbs" + }, + "theme.docs.sidebar.collapseButtonTitle": { + "message": "收起侧边栏", + "description": "The title attribute for collapse button of doc sidebar" + }, + "theme.docs.sidebar.collapseButtonAriaLabel": { + "message": "收起侧边栏", + "description": "The title attribute for collapse button of doc sidebar" + }, + "theme.docs.sidebar.navAriaLabel": { + "message": "文档侧边栏", + "description": "The ARIA label for the sidebar navigation" + }, + "theme.docs.sidebar.closeSidebarButtonAriaLabel": { + "message": "关闭导航栏", + "description": "The ARIA label for close button of mobile sidebar" + }, + "theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel": { + "message": "← 回到主菜单", + "description": "The label of the back button to return to main menu, inside the mobile navbar sidebar secondary menu (notably used to display the docs sidebar)" + }, + "theme.docs.sidebar.toggleSidebarButtonAriaLabel": { + "message": "切换导航栏", + "description": "The ARIA label for hamburger menu button of mobile navigation" + }, + "theme.docs.sidebar.expandButtonTitle": { + "message": "展开侧边栏", + "description": "The ARIA label and title attribute for expand button of doc sidebar" + }, + "theme.docs.sidebar.expandButtonAriaLabel": { + "message": "展开侧边栏", + "description": "The ARIA label and title attribute for expand button of doc sidebar" + }, + "cmfcmf/d-s-l.searchBar.placeholder": { + "message": "搜索...", + "description": "Placeholder shown in the searchbar" + }, + "cmfcmf/d-s-l.searchBar.clearButtonTitle": { + "message": "清除", + "description": "Title of the button to clear the current search input" + }, + "cmfcmf/d-s-l.searchBar.detachedCancelButtonText": { + "message": "取消", + "description": "Text of the button to close the detached search window" + }, + "cmfcmf/d-s-l.searchBar.submitButtonTitle": { + "message": "搜索", + "description": "Title of the button to submit a new search" + }, + "cmfcmf/d-s-l.searchBar.noResults": { + "message": "无搜索结果", + "description": "message shown if no results are found" + }, + "theme.blog.post.plurals": { + "message": "{count} 篇博文", + "description": "Pluralized label for \"{count} posts\". Use as much plural forms (separated by \"|\") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)" + }, + "theme.blog.tagTitle": { + "message": "{nPosts} 含有标签「{tagName}」", + "description": "The title of the page for a blog tag" + }, + "theme.blog.author.pageTitle": { + "message": "{authorName} - {nPosts}", + "description": "The title of the page for a blog author" + }, + "theme.blog.authorsList.pageTitle": { + "message": "Authors", + "description": "The title of the authors page" + }, + "theme.blog.authorsList.viewAll": { + "message": "View All Authors", + "description": "The label of the link targeting the blog authors page" + }, + "theme.contentVisibility.unlistedBanner.title": { + "message": "未列出页", + "description": "The unlisted content banner title" + }, + "theme.contentVisibility.unlistedBanner.message": { + "message": "此页面未列出。搜索引擎不会对其索引,只有拥有直接链接的用户才能访问。", + "description": "The unlisted content banner message" + }, + "theme.contentVisibility.draftBanner.title": { + "message": "Draft page", + "description": "The draft content banner title" + }, + "theme.contentVisibility.draftBanner.message": { + "message": "This page is a draft. It will only be visible in dev and be excluded from the production build.", + "description": "The draft content banner message" + }, + "theme.ErrorPageContent.tryAgain": { + "message": "重试", + "description": "The label of the button to try again rendering when the React error boundary captures an error" + }, + "theme.common.skipToMainContent": { + "message": "跳到主要内容", + "description": "The skip to content label used for accessibility, allowing to rapidly navigate to main content with keyboard tab/enter navigation" + }, + "theme.tags.tagsPageTitle": { + "message": "标签", + "description": "The title of the tag list page" + }, + "Smarter and Faster": { + "message": "更智能、更快", + "description": "Smarter and Faster" + }, + "Kylin is a high concurrency, high performance and intelligent OLAP engine that provides low-cost and ultimate data analytics experience.": { + "message": "Kylin是一款高并发、高性能、智能的DAB引擎,可提供低成本、终极的数据分析体验。", + "description": "siteConfig.tagline" + }, + "What's New": { + "message": "最新动态", + "description": "What's New" + }, + "Play in Docker": { + "message": "在 Docker 中运行", + "description": "Play in Docker" + }, + "Key Features": { + "message": "核心功能", + "description": "Key Features" + }, + "Ultra Fast Query Experience": { + "message": "超快的查询体验", + "description": "Ultra Fast Query Experience" + }, + "Provide sub-second query performance based on advanced pre-calculation technology.": { + "message": "基于先进的预计算技术提供亚秒级查询性能。", + "description": "Provide sub-second query performance based on advanced pre-calculation technology." + }, + "Support large-scale, high concurrency data analytics with low hardware and development cost.": { + "message": "以较低的硬件和开发成本支持大规模、高并发数据分析。", + "description": "Support large-scale, high concurrency data analytics with low hardware and development cost." + }, + "Model & Index Recommendation": { + "message": "模型和索引推荐", + "description": "Model & Index Recommendation" + }, + "Modeling with SQL text & automatic index optimization based on query history.": { + "message": "基于查询历史自动优化的 SQL 建模和索引优化。", + "description": "Modeling with SQL text & automatic index optimization based on query history." + }, + "More intelligent and easier for user to get started.": { + "message": "更智能、更易上手。", + "description": "More intelligent and easier for user to get started." + }, + "Internal Table with Native Compute Engine": { + "message": "内表与 Native 计算引擎", + "description": "Internal Table with Native Compute Engine" + }, + "More flexible query analysis based on internal table.": { + "message": "基于内表的更灵活的查询分析。", + "description": "More flexible query analysis based on internal table." + }, + "Integrates Apache Gluten as native compute engine, delivering over a 2x improvement in performance.": { + "message": "将 Apache Gluten 作为 Native 计算引擎,带来2x性能提升。", + "description": "Integrates Apache Gluten as native compute engine, delivering over a 2x improvement in performance." + }, + "Powerful Data Warehouse Capabilities": { + "message": "强大的数仓能力", + "description": "Powerful Data Warehouse Capabilities" + }, + "Advanced multi-dimensional analysis, various data functions.": { + "message": "先进的多维分析,大量 SQL 函数。", + "description": "Advanced multi-dimensional analysis, various data functions." + }, + "Support connecting to different BI tools, like Tableau/Power BI/Excel.": { + "message": "支持连接不同的 BI 工具,如 Tableau/Power BI/Excel。", + "description": "Support connecting to different BI tools, like Tableau/Power BI/Excel." + }, + "Streaming-Batch Fusion Analysis": { + "message": "流批融合分析", + "description": "Streaming-Batch Fusion Analysis" + }, + "New designed streaming/fusion model capability, reducing data analysis latency to seconds-minutes level.": { + "message": "新设计的流批融合模型能力,将数据分析延迟降低到秒级。", + "description": "New designed streaming/fusion model capability, reducing data analysis latency to seconds-minutes level." + }, + "Support fusion analysis with batch data, which brings more accurate and reliable results.": { + "message": "支持融合分析与批量数据,带来更准确的、更可靠结果。", + "description": "Support fusion analysis with batch data, which brings more accurate and reliable results." + }, + "The new modeling process is concise, allowing users to define table relationships, dimensions, and measures on a single canvas.": { + "message": "新建模流程简洁,允许用户在单个画布上定义表关系、维度和度量。", + "description": "The new modeling process is concise, allowing users to define table relationships, dimensions, and measures on a single canvas." + }, + "Brand New Web UI": { + "message": "全新 Web UI", + "description": "Brand New Web UI" + }, + "Apache Kylin Overview": { + "message": "Apache Kylin 介绍", + "description": "Apache Kylin Overview" + } +} diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2022-07-29-RoadMap_of_ApacheCon/img/1.png b/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2022-07-29-RoadMap_of_ApacheCon/img/1.png new file mode 100644 index 0000000000..461c1eec0a Binary files /dev/null and b/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2022-07-29-RoadMap_of_ApacheCon/img/1.png differ diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2022-07-29-RoadMap_of_ApacheCon/img/2.png b/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2022-07-29-RoadMap_of_ApacheCon/img/2.png new file mode 100644 index 0000000000..60ada128dc Binary files /dev/null and b/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2022-07-29-RoadMap_of_ApacheCon/img/2.png differ diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2022-07-29-RoadMap_of_ApacheCon/img/3.png b/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2022-07-29-RoadMap_of_ApacheCon/img/3.png new file mode 100644 index 0000000000..1f34f327fc Binary files /dev/null and b/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2022-07-29-RoadMap_of_ApacheCon/img/3.png differ diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2022-07-29-RoadMap_of_ApacheCon/img/4.png b/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2022-07-29-RoadMap_of_ApacheCon/img/4.png new file mode 100644 index 0000000000..3c8f8b5a12 Binary files /dev/null and b/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2022-07-29-RoadMap_of_ApacheCon/img/4.png differ diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2022-07-29-RoadMap_of_ApacheCon/img/5.png b/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2022-07-29-RoadMap_of_ApacheCon/img/5.png new file mode 100644 index 0000000000..c4e2c9da3d Binary files /dev/null and b/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2022-07-29-RoadMap_of_ApacheCon/img/5.png differ diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2022-07-29-RoadMap_of_ApacheCon/img/6.png b/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2022-07-29-RoadMap_of_ApacheCon/img/6.png new file mode 100644 index 0000000000..f3f85f430a Binary files /dev/null and b/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2022-07-29-RoadMap_of_ApacheCon/img/6.png differ diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2022-07-29-RoadMap_of_ApacheCon/img/7.png b/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2022-07-29-RoadMap_of_ApacheCon/img/7.png new file mode 100644 index 0000000000..98704ab4de Binary files /dev/null and b/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2022-07-29-RoadMap_of_ApacheCon/img/7.png differ diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2022-07-29-RoadMap_of_ApacheCon/img/8.png b/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2022-07-29-RoadMap_of_ApacheCon/img/8.png new file mode 100644 index 0000000000..877d5a603f Binary files /dev/null and b/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2022-07-29-RoadMap_of_ApacheCon/img/8.png differ diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2022-07-29-RoadMap_of_ApacheCon/index.md b/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2022-07-29-RoadMap_of_ApacheCon/index.md new file mode 100644 index 0000000000..da90d6dc7b --- /dev/null +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2022-07-29-RoadMap_of_ApacheCon/index.md @@ -0,0 +1,160 @@ +--- +title: Kylin 5.0 的未来规划 +slug: roadmap_of_kylin_50_cn +authors: xxyu +tags: [apachecon, roadmap, kylin5] +hide_table_of_contents: false +date: 2022-12-09T10:00 +--- + +:::tip Before your read +**Target Audience** +- 对新一代 Kylin 感兴趣的开发者和用户 +- 对 OLAP 技术有兴趣的大数据爱好者 + + +**What will you learn** +- 了解最新版本的 Kylin 的设计思路 +- 了解最新版本的 Kylin 的开发路线和发布计划 + +💬 Kylin 5.0 相对于上个版本有了更长足的进步,尤其是在使用场景的扩充和构建查询的性能提升上。 +这篇 Kylin 5.0 社区路线图,将帮助大家了解社区开发者在面对 Kylin 4 尚未解决的问题,是如何在 Kylin 5 上进行全面的重新设计并且实现新功能,并且会分析这些新功能,诸如“Native Engine”、“Schem Change”、“可计算列”、“明细索引”给用户带来的全新体验。 +::: + +<!--truncate--> + +## 今天的 Apache Kylin + +### Apache Kylin 是什么? + Apache Kylin™ 是一个开源的分析型数据仓库,为 Hadoop 等大型分布式分析平台之上的超大规模数据集(PB级)通过标准 SQL 查询及多维分析 ( OLAP ) 功能,提供亚秒级的交互式分析能力。 + +### Apache Kylin 的基本原理 + +Cube 是 OLAP ( Online Analytical Processing ) 的核心数据结构,把维度和度量抽象为一个多维模型,赋予了OLAP 新的数据组织和存储形式,并可以快速、高效地完成 OLAP 的多维分析操作。 + +我们可以想象有一个三个维度的 Cube,它包含了商品种类、时间和地点。在存储中我们可以把某一个具体的小方块想象成一个特定维度组合的度量,左下角的小方块存储了(浙江,2010 第一季度,书籍)这个组合下我们关心的业务指标, +s这可能是成交总量、平均价格等等。基于这样的数据结构,我们可以完成钻取、上卷、切片、切块和旋转的操作,这样就可以进行一些数据分析、数据探索,帮我们去解答这些业务的问题。并且由于不需要现场进行扫描和聚合,所以查询响应一般很快。 + + + +Apache Kylin 通过构建引擎和查询引擎来分别生成和查询预计算数据文件,并且基于分布式计算引擎如 Apache Spark 来扩展构建引擎和查询引擎的计算能力。对于用户,Kylin 提供不同的接口如 SQL/HTTP/BI/Excel 等方式来进行数据分析,可以使得用户可以对 PB 级数据集实现秒级查询。 + +### 当前版本的限制 +- 模型修改的使用方式有限制,对于建好的 Model/Cube,不能随意添加维度和度量,需要先清理现有的预计算索引(Cuboid)才能添加;为了增加新的度量和维度,用户需要创建新的 Cube,增加了管理成本,并且浪费了计算和存储资源。 +- 预计算索引的构建不够灵活,用户只能选择构建全部的预计算索引,一旦构建完成用户无法自主选择添加和删除部分预计算索引(只能通过 Cube Planner 的算法进行筛选)。 +- 构建和查询性能仍有提升空间,Kylin 使用 Apache Spark 作为执行/计算引擎,没有利用到向量加速、指令级优化等技术。 +- 维度数量有上限,最多支持 63 个维度。这是由于元数据存储中是用 Long 型来保存的 Cuboid ID 而造成的。 +- 长期使用后元数据存储膨胀,非核心元数据(任务历史等)会占用大量的空间,造成元数据读取写入性能下降,从而引起 Kylin 整体服务性能不佳。 + +## Kylin 5 的设计思考 + +### Kylin 5 的整体目标 + +我们希望 Kylin 5 是一个统一、灵活、高性能、可扩展、云原生的大数据分析平台,可以做到在一个平台上完成很多的数据分析工作,能够对接各种数据源,支持多种查询接口,也可以可插拔地替换各种计算引擎,包括支持性能有明显提升的 Native Engine,可以为现代企业的海量数据上的数据分析和指标管理提供一个坚实可靠的底座。 + +同时我们也希望未来 Kylin 5 可以支持在 K8S 上部署,Kylin 5 将以微服务的方式进行拆分和部署,应对不同的负载可以支持灵活快捷地进行扩缩容。 + + + +### Kylin 5 的设计方向 + +#### No.1 重新设计核心元数据 + +这里涉及到元数据 Schema 和元数据存储的升级,通过对元数据进行重构使得 Kylin 的使用限制得以消除;元数据的存储会把元数据进行结构化(而不是现在的键值存储形式),以提升元数据读写性能。 + +#### No.2 核心功能升级改造 + +基于新的元数据的设计,我们进一步去改造构建引擎和查询引擎,使我们能够更加灵活的创建和刷新索引,在添加新的度量和维度时,可以不用清空现有的所有的预计算索引,给 Kylin 带来使用灵活性的提升。 + +Kylin 5 抽象了索引的类型以提供索引类型的可扩展性,Kylin 5 可以同时支持聚合查询和明细查询。 + +除此以外,还计划会支持更多类型的连接关系(例如多对多)、支持索引的多级分区、支持拉链表、支持(自动适应)表元数据变更等生产场景。 + +#### No.3 计算引擎性能提升 + +Kylin 需要使用分布式计算引擎来构建和扫描预计算索引文件,计算引擎的速度快慢很大程度上影响用户的体验。对于 Kylin 5.0,Kylin 5 将继续使用 Apache Spark 作为基础的计算引擎,除此以外,社区开发者正在尝试使用一些 Native Engine 来扩展(或者代替) Spark,包括 Gluten + Clickhouse 或者 Datafusion。 + + + +## Kylin 5 开发路线图 +### 元数据 Schema 升级 +1. 将 Model 和 Cube 二者合并为新的 Model ,Cube 将从元数据和界面中被舍弃;在 Kylin 5 中,用户不再需要先创建模型(Join Relationship),再定义 Cube(维度和指标),而是将两个步骤合并为了一个步骤(即创建 Model),简化了流程,方便了用户。 +2. 新增元数据 Index 和 Layout 。其中新增的 Index 可以提升 Kylin 的扩展性,除聚合索引外还添加了明细索引;而 Layout 可以使得对于指定 Index 可以具有不同的数据特征(Shard Key 和 Sort Key),这样可以在查询时使用最合适的 Layout 来进行查询加速。并且新的 Index 没有 63 个维度的数量限制。 +3. 引入 IndexPlan,IndexPlan 根据用户的模型设计和静态规则生成索引(RuleBasedIndex),并且这些 IndexPlan 也可以用来适应各种因素(内置策略、外部修改)引起的索引增减:一方面 IndexPlan 的引入使得应对模型变更或者表元数据的变更变得简单,另一方面 IndexPlan 也为基于代价的索引优化器提供了设计上的支持。 +4. 将 CubeInstance 改为 DataFlow。 +5. 新增元数据审计日志,可以用于实现元数据操作审计、开发者调试和元数据缓存的节点间同步机制。 + + + +#### 元数据同步机制升级 +和之前的基于广播的元数据同步机制不同的是,在新的元数据同步机制下,每个节点会基于元数据变化信息进行主动同步,让元数据缓存同步的可靠性得到提升。(相关技术细节请期待后续的系列技术文章) + + +### 灵活的索引管理和支持 Schema Change +相比之前的版本(Kylin 4.X),Kylin 5 提供了灵活的索引管理能力,支持对部分索引进行构建刷新和删除等。 + +- 场景一:随着数据分析师的查询 Pattern 变化,用户的大量新增查询无法被 Excatly Match,故只能被 base cuboid 所回答,所以用户可能希望手动增加(或者减少)某一些索引,来适配新的查询。 + + 在 Kylin 4 中,用户只能选择克隆了一个新的 Cube(在 Kylin 5 上 用户创建的),然后在新的 Cube 上进行编辑然后触发任务重头构建。但是在 Kylin 5 上,用户可以选择在现有的 Model 上面直接创建新的 Index,使用十分快捷。 + +- 场景二:随着数据分析师关注业务方式的变化,用户要求在 Cube 上增加新的维度和度量,从而增加对商业行为的洞见。 + + 在 Kylin 4 中,用户同样也不能在原有的 Model/Cube 上进行编辑以增删维度和度量;但是在 Kylin 5 上,借助新的元数据设计(Model & IndexPlan),直接编辑以增删维度和度量这种行为变得轻而易举,任何随着模型编辑而失效的 Index 会自动变为无效,并且会在下次垃圾清理作业中被清除,任何随着模型编辑而需要构建的 Index 可以通过一键提交任务构建出来。 + +- 场景三:随着公司业务演进,数仓事实表和维度表的 Schema 也很有可能随之变化,Kylin 管理员希望 Kylin 的 Table/Model 能够平滑地同步来自数仓的元数据变更,并且尽可能减少 Schema Change 带来的手动修复工作。 + + 在 Kylin 4 中,大部分 Schema Change 场景同样不能很好地被处理,Kylin 管理员可能被迫选择重新操作整个建模流程以同步新的表元数据。对于 Kylin 5,大部分 Schema Change 场景(包含增加删除列、重命名列、修改列的数据类型等)都能被很好地识别并且尽可能地自动化处理冲突。 + + + +### 明细查询 +明细查询一直是 Kylin 的短板,最新的 Kylin 通过物化(部分)平表来加速明细查询,同时可以根据查询语句特征来设置 ShardKey 和 SortKey 来优化性能,从而可以提升高基数列过滤查询的性能。这个功能在 Kylin 5 初步称为 Table Index。 + +### 可计算列 +通过在模型上定义表达式,Kylin 支持了轻量 ETL,用户可以把新的表达式当做普通的列,基于它们定义新的维度和度量,使得这部分的计算工作也被预计算引擎所加速。 + +### 计算引擎 Native 化 +Kylin 4 依赖 Spark 作为构建和查询引擎,而 Spark 的基本算子如 HashAgg、TableScan 等,近年来性能提升改变不大。为了进一步加强 Kylin 的构建和查询性能,Kylin 5 计划引入了全新的 Native Engine。 + +在 Native Engine,社区有不同的选择,其中选择之一是 Gluten,在内部测试中,对于 TPC-H 查询集,Gluten + CH 的组合相对于 Apache Spark 有一倍以上的性能提升。 + + + +### 全新的界面交互 +前端界面全新升级,优化用户操作,通过更直观的画布建模方式,让用户一目了然,降低了使用门槛。 + +在下面的截图中,我们能看到建模的主要工作都集中在一个画布里完成,包括了定义 Join Relationship、Dimension、Measure、Computed Column、Partition Column 等。 + + + +### Kylin on K8S +Kylin 5 会进行微服务化、容器化改造,Kylin 现有的 Job/Query Node 会拆分为 DataLoading,Query,Metadata 等多个服务模块,使得每个模块可以单独扩容以适应实际需求。 + +此外,计算引擎会依赖于 K8S,数据存储会依赖于对象存储如 S3,元数据服务可以依赖于元数据服务(例如 AWS Glue)。相信基于新的 Kylin on K8S 方案,未来在公有云上部署 Kylin 服务将变得部署快速、低成本和低门槛。 + + + +### 详细的查询历史 +Kylin 5 新增了详尽的查询历史信息,为每个查询存储了 Kylin 和 Spark 和查询相关的详尽的 Metrics,这些数据作为索引优化/剪枝策略的输入,为社区未来开发 Cube Planner(Cost based Index Optimizer) 提供了支持。 + +## Kylin 5 发布时间表 +1. ✅核心功能开发完毕:2022/09,完成 +2. ✅代码正式开源到 Github:2022/09 - 2022/10,完成 +3. ⌛基于代价的索引优化器:2022/10,开发中 +4. ✅用户手册和开发者文档:2022/10 - 2022/12,基本完成 +5. ✅核心功能测试:2022/10 - 2022/12,基本完成 +6. ⌛技术预览版发布:2023/01,进行中,期待您的试用~ +7. Kylin 5 正式发布:2023(依赖试用反馈和问题修复速度) +8. Kylin 5 集成 Native Compute Engine:2023(依赖 Gluten 项目和 Datafusion 项目的进展) + +### Kylin 5 官网和试用链接 +- 初版 Kylin 5.0 用户文档:https://kylin.apache.org/5.0 +- Kylin 5.0 预览版 Standalone Docker Image:https://kylin.apache.org/5.0/docs/quickstart/intro + +## 社区未来展望 + +我们在上面介绍了很多 Kylin 5 的新特性,但是希望读者了解有更多的特性由于篇幅原因没有介绍到,还有更多的特性在设计和开发中。 + +我们希望随着 Kylin 5 的正式发布,Kylin 5 将是一个性能有大幅提升,用户使用体验得到显著改善,支持的业务场景得到广泛扩充的 OLAP 平台。在这些基本目标完成后,Native Engine 和 Kylin 的云上部署都会在 Kylin 社区的开发计划上,直到 Kylin 5 成为一个统一、灵活、高性能、可扩展、云原生的大数据分析平台。 + +期待大家对 Kylin 5 技术预览版的试用和反馈,一起帮助 Kylin 变得越来越好。 \ No newline at end of file diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2022-12-18-Introduction_of_Metadata/index.md b/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2022-12-18-Introduction_of_Metadata/index.md new file mode 100644 index 0000000000..8272bf5962 --- /dev/null +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2022-12-18-Introduction_of_Metadata/index.md @@ -0,0 +1,322 @@ +--- +title: Metadata 介绍 +slug: introduction_of_metadata_cn +authors: pfzhan +tags: [metadata, kylin5] +hide_table_of_contents: false +date: 2022-12-18T17:00 +--- + +:::tip Before your read +**Target Audience** +- Kylin 5.0 的开发者和用户 + +**What will you learn** +- 了解 Kylin 5.0 的元数据 Schema 设计和原理 + +💬 Kylin5 对[元数据的设计](https://github.com/apache/kylin/blob/doc5.0/website/blog/2022-12-18-Introduction_of_Metadata/protocol-buffer/metadata.proto) +做了比较大的调整,本文将针对这些调整做比较详细的介绍。 +::: + +<!--truncate--> + +# Introduction of Metadata + +Kylin5 对元数据的组织结构做了比较大的调整,本文将针对这些调整做比较详细的介绍。相比于 Kylin4,Kylin5 的元数据的一个显著特点是项目级隔离,也即每个项目彼此独立、互不干扰。本文会从项目开始,分别展开Table、Model、IndexPlan、Segment、Job 各个部分的内容。更宽泛地说,Kylin5 的元数据还包括元数据更新审计日志(AuditLog)、事务表(Epoch)、权限(ACL)、查询历史(Query History) 等内容。所有的这些内容都很重要,但作为入门级的介绍,本文不涉及更宽泛的内容,而是尽量把篇幅控制在元数据中最为基础的那部分,以帮助更多的开发者快速地了解和参与到 Kylin5 的研发。当然,作为一篇入门级介绍性文章,非研发人员阅读也能有所收获。接下来让我们切入主题。 + +## **Overview** + +当启动一个 Kylin5 的实例在这个实例上进行了一些常规操作,创建项目、加载表、建模、编辑聚合组、构建索引等操作后,通过执行脚本 `{KYLIN_HOME}/metadata.sh backup` 可以得到类似于如下结构的元数据树状图。 + +``` +. +├── UUID +├── _global +│ ├── project +│ │ ├── def.json +│ │ └── ssb.json +│ ├── resource_group +│ │ └── relation.json +│ ├── sys_acl +│ │ └── user +│ │ └── ADMIN +│ ├── user +│ │ └── ADMIN +│ └── user_group +│ ├── ALL_USERS +│ ├── ROLE_ADMIN +│ ├── ROLE_ANALYST +│ └── ROLE_MODELER +├── def +│ ├── dataflow +│ │ └── de52affe-f280-dcd1-be78-7865ff149669.json +│ ├── dataflow_details +│ │ └── de52affe-f280-dcd1-be78-7865ff149669 +│ │ ├── 0b36355d-03df-8b02-aaf9-c2ab00e30456.json +│ │ ├── 743c5345-a3cd-9acf-2c90-c5bcec61c600.json +│ │ └── 7cbe4459-cbbd-8b8f-bb88-6a54c24d76ce.json +│ ├── execute +│ │ ├── 1c7dd6c1-6da5-72e6-a2fe-e87e5c764502-de52affe-f280-dcd1-be78-7865ff149669 +│ │ ├── 383edce9-fad4-eba9-a498-0f289f1f1a79-de52affe-f280-dcd1-be78-7865ff149669 +│ │ ├── 88400e7b-104a-9117-343d-cc3047b49983 +│ │ ├── 95300d93-da1e-6505-b710-2771c724fa63-de52affe-f280-dcd1-be78-7865ff149669 +│ │ ├── c11e4632-578b-1007-d3e6-65afca6f003c-de52affe-f280-dcd1-be78-7865ff149669 +│ │ └── e1731c9e-d70b-e2eb-0fca-6be24751fb72 +│ ├── index_plan +│ │ └── de52affe-f280-dcd1-be78-7865ff149669.json +│ ├── job_stats +│ │ └── 1671292800000.json +│ ├── model_desc +│ │ └── de52affe-f280-dcd1-be78-7865ff149669.json +│ ├── table +│ │ ├── SSB.CUSTOMER.json +│ │ └── SSB.LINEORDER.json +│ └── table_exd +│ ├── SSB.CUSTOMER.json +│ └── SSB.LINEORDER.json +├── ssb +│ ├── dataflow +│ │ ├── 407f1b4e-e5d7-6c1d-3697-a3deaffd0f6b.json +│ │ └── 91b2007b-112f-f98e-b967-c2fe26c6761c.json +│ ├── dataflow_details +│ │ └── 91b2007b-112f-f98e-b967-c2fe26c6761c +│ │ └── 19211ed7-9c05-cc8e-9d05-7b64d0b90cf8.json +│ ├── execute +│ │ ├── 5dde1d4b-f60b-7601-bdda-4d20493b324d-91b2007b-112f-f98e-b967-c2fe26c6761c +│ │ ├── add2d7c6-4f51-1144-b05f-a28da0d42e1d +│ │ └── c144cb8f-28c8-833e-3990-59e51bd3f7f8 +│ ├── index_plan +│ │ ├── 407f1b4e-e5d7-6c1d-3697-a3deaffd0f6b.json +│ │ └── 91b2007b-112f-f98e-b967-c2fe26c6761c.json +│ ├── job_stats +│ │ └── 1671292800000.json +│ ├── model_desc +│ │ ├── 407f1b4e-e5d7-6c1d-3697-a3deaffd0f6b.json +│ │ └── 91b2007b-112f-f98e-b967-c2fe26c6761c.json +│ ├── table +│ │ ├── SSB.DATES.json +│ │ └── SSB.LINEORDER.json +│ └── table_exd +│ ├── SSB.DATES.json +│ └── SSB.LINEORDER.json +``` + +从上面的这个树形结构很容易看出 Kylin5 的元数据是项目级隔离的,_global 这个项目相对特殊,它用来存储系统级别的信息如当前实例的项目元数据、ACL 权限相关的元数据、用户以及用户组等信息。权限相关的部分略去不谈,将重点放在单个项目的元数据组织结构上。这份元数据只包括两个项目 ssb 和 def,它们的组织结构完全相同,接下来将一一展开介绍,这里先简要地说明每个目录的作用。 + +- table: 记录该项目加载的所有表的元数据信息 +- table_exd: 记录 table 目录下表对应的扩展描述性信息 +- model_desc: 记录模型的元数据信息 +- index_plan: 记录索引相关的元数据信息 +- dataflow: 记录 segment 相关的元数据信息 +- dataflow_details: 这是个目录,里面每个文件记录的是已构建索引的描述性信息 +- execute: 记录构建任务相关的元数据信息 +- job_status: 记录构建任务执行状态的元数据信息 + +将 execute 和 job_status 对应的信息排除在外,那么其它的部分是组成一个项目最为核心的元数据信息。上面这个树状图是备份元数据之后的结果,实际上它们都存在同一张元数据表中,所有的元数据都是一条条的表记录,记录的绝对路径就是它在原数据表中的 meta_key,比如,/ssb/table/SSB.DATES.json 这个绝对路径就代表了ssb 项目加载的表 SSB.DATES。 + +## **Table Description** + +表的描述性文件信息包括 table 和 table_exd 两个文件夹下的内容。table/SSB.DATES.json 是从数据源加载到 Kylin5 系统中生成的表基础描述性信息,而 table_exd/SSB.DATES.json 则是表的扩展信息。随着 Kylin5 功能的扩展,它可能越来越丰富。就目前来说,扩展信息包括表采样信息和查询命中次数信息。 + +在表的描述性信息中,大部分属性很清晰,这里主要对几个解释一下含义。 + +- `source_type` 表的源信息,ISourceAware 这个类中定义了一些常规的数据源的类型。 +- `table_type` 表的类型,来源于表或者视图。 +- `transactional` 事务表标志。 +- `increment_loading、top、rangePartition` 已经属于废弃字段。 +- `query_hit_count` 已移到表扩展信息中。 +- 关于表的描述性信息中还有保留了一些快照相关的信息,这是不太合理的,因为快照可能在每次构建时候都自动更新。 + +值得注意的是,这里的表的描述性信息是项目级别的,而实际模型在引用到这些元数据的时候会利用模型上添加的可计算列信息加以扩展,从而使得表上的列会增加,但这部分内容是在内存中存在的,不会被保存到元数据库中。关于这部分内容,接下来的模型部分会给予进一步说明。 + +## **Model Description** + +模型是 Kylin5 的一个核心概念,可以认为它是一系列相关业务的抽象,在这个抽象中包含了一个或者多个业务模式 (对应到索引)。模型包含的概念比较多,如维度、度量、可计算列、普通列、表的关联关系、事实表、维表、星型模型、雪花模型、星座模型(暂不支持)等,大部分概念在维度建模理论中都有论述,本文不再做说明。这里仅介绍 Kylin5 中特有的概念:**普通列** 和**可计算列**。 + +先说**可计算列**(ComputedColumn),借用 Kylin5 手册中的一段话来说明可计算列。 + +> 可计算列是为了充分利用 Kylin 的预计算能力而在模型中预先定义的数据列。通过将相对复杂的在线计算转换成基于可计算列的预计算,查询的性能将会得到大幅提升。此外,可计算列支持将数据的转换、重定义等操作预先定义在模型中,增强数据语义层。通过定义可计算列,用户可以重用已有的业务逻辑代码。 +> + +在模型定义可计算列时,会同时往普通列中添加一个同名的列,目前可计算列只能定义在事实表上。可计算列之后就可以在维度和度量定义时使用。前文讲 Table 这部分内容的时候提到,模型使用的表扩展了可计算列,它是通过模型的 getExtendedTables 方法在使用 table 之前扩展进来的,所以模型使用的是增强了语义信息的表。 + +**普通列**(NamedColumn) 的来源有两个,一是前文已经说明可计算列的定义会同时增加一个普通列,二是来源于建模时添加的事实表和所有被关联上的维表。普通列有个属性 status 用于标记这个列是维度列(DIMENSION)、被删除的列(TOMB)、还是仅仅是个普通列(EXIST)。当和可计算列同名的列的 status 属性是 DIMENSION 则表明可计算列被定义成了维度。 + +### **Significant Change** + +Kylin5 中定义了**模型的重大变更**,以确定是否需要执行一系列的后续操作。重大变更会删除无价值的维度和度量,可能会触发 Segment 重新构建,而且模型的 semantic_version 属性在每次发生重大变更后都会自增。以下任意一种情况发生,都会被认定为模型已经发生重大变更。 + +- 分区列、多级分区列的变化 +- 事实表变更 +- 模型中表的关联关系变更 +- 模型的过滤条件发生变化 +- 模型关联的维表是否预计算属性被更改 + +### **Broken Model** + +由于 model_desc、index_plan、dataflow 之间存在一一对应的关系,因此这三者中的任意一个遭到破坏,模型都会以 broken 状态展示出来。损坏的模型需要用户干预去修复。模型什么时候会损坏?一般来说,模型损坏来源于重载表操作,当数据源中表的列被删除、或者表的列数据类型产生无法兼容的变更、表被删除等情况发生时,重载表有可能导致模型损坏。**因此,在重载表或者做一些相对比较危险性的操作时,记得要先备份元数据。** + +模型损坏时会触发哪些操作?优化建议全部清空、dataflow 上会记录失败的原因。失败原因分为三类:由 EVENT 触发的、由 SCHEMA 触发的、其他未知问题触发的 (NULL)。 + +- EVENT 导致的失败 +- SCHEMA 导致的失败会删除所有已经构建好的 segment。 +- NULL 导致的失败 + +## **IndexPlan** + +IndexPlan 是 Kylin5 中用来组织 Index/Layout 的元数据,它里面包含两个最重要的信息 RuleBasedIndex 和 Indexes,前者用于管理聚合组索引,后者用于管理被物化的索引。在介绍这两个概念之前,我们先介绍一下 Index 和 Layout 这组概念。 + +### **Index & Layout** + +Index 是一个集合概念(后面翻译为索引),它将一类 Layout 管理在一起,这些 Layout 需要满足维度和度量的元素集合相同,但它们的排列不同,或者它们的 shardByColumn 不一样。Layout 时这个集合中的一个具体的排列。 + +- Index 的 id 是 10000 的整数倍 +- Layout 与 Index 之间的关系: 参考下面的例子会比较清楚 +- 只要不引起歧义,可使用 Index 来代指一个具体的 Layout +- 同样的 Layout 被删除后如果再生成出来,那么它的 ID 是全新的 + +``` +假设模型有3个维度分别是{1, 2, 3} ,2个度量分别是{100000, 100001}, +当使用全部维度和度量时,Index 的 Bitset 包含{1, 2, 3, 100000, 100001}; +但是生成的 Layout 可以有很多个,比如: + {col_order = [1, 2, 3, 100000, 100001] , shard_by_column = []}, + {col_order = [1, 2, 3, 100000, 100001] , shard_by_column = [1]}, + {col_order = [2, 1, 3, 100000, 100001] , shard_by_column = []}, + {col_order = [3, 1, 2, 100000, 100001] , shard_by_column = [2]} +注: col_order 是维度的一个排列再加上度量。shard_by_column 影响查询效率。 + Kylin5在大部分情况下使用的是id值,维度、度量、Layout都是这样。 +``` + +当前 Kylin5 设计中,Layout 定义了两个属性 manual 和 auto,manual 用来标记索引是用户自定义的,auto 用来标记索引是自动化程序或者脚本生成的,开发者可自行扩展。定义两个属性是为了更好的可扩展性,比如索引先由程序生成出来,之后用户又编辑了聚合组也生成了一个相同的索引,但又不希望这个信息被覆盖掉。按照这两个属性可以将索引划分为用户自定义索引和自动化索引。一般来说程序自动生成的索引比较灵活,也能够清晰的看到索引的组成部分(元数据被物化下来了,或许在不久的将来 Kylin5 将所有的索引都物化下来也未可知),开发者可自行扩展。 + +按照索引是否预计算聚合将索引分为聚合索引和明细索引。一个不太优雅且通俗易懂的说法,聚合索引就是带度量的索引,明细索引相反。此外,Layout 还定义了一个属性 base 用于标注索引是否是基础索引,基础索引能够尽最大能力避免查询下压到其他计算引擎,比如 Kylin5 自带的 Spark 下压引擎,用户可自行扩展其他计算引擎。 + +### **RuleBasedIndex** + +RuleBasedIndex 里面包含多个聚合组(NAggregationGroup),每个聚合组可以定义自己的生成规则,具体包括必须维度、联合维度、层级维度以及最大维度组合数。RuleBasedIndex 也可以定义一个模型级别的最大维度组合数。当删除 RuleBasedIndex 中的索引时,索引会被加入到 `layout_black_list` 中来保证编辑聚合组不至于导致索引出现 ID 错乱的情况。如果出现模型重大变更 `layout_black_list` 会被清空,整个聚合组会重新生成索引,需要重刷 Segment 数据。 + +```json +"rule_based_index" : { + "dimensions" : [ 0, 10, 16, 26, 17, 19 ], + "measures" : [ 100000, 100001, 100002, 100003, 100004 ], + "global_dim_cap" : null, + "aggregation_groups" : [ { + "includes" : [ 0, 10, 16, 26, 17, 19 ], + "measures" : [ 100000, 100001, 100002, 100003, 100004 ], + "select_rule" : { + "hierarchy_dims" : [ ], + "mandatory_dims" : [ 10, 0, 16 ], + "joint_dims" : [ [ 17, 19 ] ] + }, + "index_range" : "EMPTY" + } ], + "layout_id_mapping" : [ 10001, 20001, 30001, 40001 ], + "parent_forward" : 3, + "index_start_id" : 10000, + "last_modify_time" : 1671335454291, + "layout_black_list" : [ ], + "scheduler_version" : 2, + "index_update_enabled" : true, /* streaming 相关,可以忽略 */ + "base_layout_enabled" : true /* 是否生成包含所有聚合组维度度量的大索引 */ +} +``` + +### **Indexes** + +Indexes 属性用于管理用户自定义明细索引、基础明细索引、基础聚合索引以及用户通过扩展 Kylin 自动生成的索引。这类索引区别于聚合组生成的索引的显著特点是所有信息一目了然。对于开发 Kylin 功能以及排查有些查询问题特别方便。需要注意的是,基础聚合索引和基础明细索引会随着编辑模型添加维度、度量重新生成,之后原来的基础索引就变成了普通索引并且会变为锁定状态,在新的基础索引没有构建好之前仍然可以供用户查询。构建好之后,锁定状态的索引不会自动删除,需要用户主动触发删除。 + +```json +"indexes" : [ { + "id" : 0, + "dimensions" : [ 0, 10, 16, 17, 19, 22, 26 ], + "measures" : [ 100000, 100001, 100002, 100003, 100004 ], + "layouts" : [ { + "id" : 1, + "name" : null, + "owner" : null, + "col_order" : [ 0, 10, 16, 17, 19, 22, 26, 100000, 100001, 100002, 100003, 100004 ], + "shard_by_columns" : [ ], + "partition_by_columns" : [ ], + "sort_by_columns" : [ ], + "storage_type" : 20, + "update_time" : 1671335046320, + "manual" : false, /* 聚合组定义的索引 */ + "auto" : false, /* 自动化程序生成的索引 */ + "base" : true, /* 基础索引 */ + "draft_version" : null, /* 废弃属性 */ + "index_range" : null /* streaming 相关暂时可忽略 */ + } ], + "next_layout_offset" : 2 +}] +``` + +最后介绍 IndexPlan 的其他重要属性来结束这部分内容。 + +- `next_aggregation_index_id` 记录新的聚合索引可以分配的 ID; +- `next_table_index_id` 记录新的明细索引可以分配的 ID; +- `approved_additional_recs、approved_removal_recs` 非开源功能,无需关注; +- `retention_range、engine_type`、 废弃属性 + +## **Segment** + +这一部分是对 Segment 数据存储的描述性性信息,包括 dataflow 和 dataflow_details 两部分。其中,dataflow 用于存储 segment自身的描述性信息,而 dataflow_details 则存储的是每个 segment 已经构建的索引的一些描述性信息。 + +### **Dataflow** + +这部分元数据主要描述了Segment的总体信息,分类说明一些重要的信息。 + +status 状态信息包括:ONLINE、OFFLINE、WARNING。ONLINE 就是模型在线可供查询,OFFLINE 反之。模型 OFFLINE 的场景包括: + +``` +刚新建的无分区列的模型(构建好会自动 ONLINE) +模型没有任何 segment,模型自动 OFFLINE +克隆出来的新模型默认 OFFLINE +用户主动下线模型 +``` + +值得关注的是 WARNING 状态的模型,它表明模型存在异构,这是因为 Kylin5 允许对一个模型并发构建以及在线的 Schema 变更。与前面模型部分已经提到重大变更不同,这里的变更对已有的索引和 Segment 影响不大,比如:删掉了一些索引、增加了一些维度度量以及索引、添加了一些可计算列等。Segment 中的索引异构分几种情况: + +- 对部分 segment 删除索引导致的; +- 新增索引导致的; +- segment 中构建的索引依赖了不同的平表 + +segments 这个属性刻画了模型中存在多少 segment,每个 segment 是增量构建还是全量构建、构建的时间范围多大、segment 的 min/max 统计信息。除此之外,dataflow 上还记录了一些查询统计信息,设计上的缺陷,不应该把查询统计信息和描述性信息耦合在一起。 + +### **DataflowDetails** + +这一部分主要用于记录每个 segment 构建了哪些索引以及每个索引构建好的数据的一些统计信息。当查询到具体的 segment 中的索引时,查询结果会统计并展示这些信息的汇总结果。dataflow_details 这个目录中的数据在没有构建索引的情况下是不存在的,但是 dataflow 里面的信息则不一样,即使没有添加任何segment,也会有一个与模型对应的文件,只是里面的属性 segments 为空数组。 + +```json +"layout_instances" : [{ + "layout_id" : 1, + "build_job_id" : "xxxxx", + "rows" : 4942, + "byte_size" : 138432, + "file_count" : 2, + "source_rows" : 18416, + "source_byte_size" : 0, + "partition_num" : 1, + "partition_values" : [ ], + "is_ready" : false, + "create_time" : 1671335217776, + "multi_partition" : [ ] +}] +``` + +## **Task & Job** + +Task 这一部分与构建任务相关,包括 execute 和 job_status 两部分。相比于以上部分,这部分内容更多的被用在构建任务报错后定位问题。这边不做详细讨论,有兴趣可以直接参考代码理解这些内容。重点提一下,execute 下的 job 的 uuid 可能很长,那是因为它后缀了模型的 uuid,起作用是在构建时避免项目锁的抢占,它能够提升整个系统的稳定性和并发构建的能力。 + +## **Brief Summary** + +最后介绍一下单个项目的描述性信息,它里面的属性和项目级设置相关,这里挑一些重要的作介绍。default_database 项目默认数据库,通过设置默认数据用户可以在查询的时候对默认数据库中的表不用加上数据库前缀。override_kylin_properties 记录项目覆盖的配置信息,目前 Kylin5 提供的查询相关的配置,基本上都可以定义到项目级别。segment_config 中的配置在 Segment 合并时使用。 + +总而言之,Kylin5 的新一代元数据设计旨在提高系统的易用性、可扩展性,比如:IndexPlan 的抽象、Index & Layout 的设计、索引的属性 manual & auto 的设计等。这些都开发者进一步探索留下了广阔的空间。除此之外,Kylin5 也提供了诸多新颖的特性,如: + +- 可计算列丰富了预计算能力 +- AuditLog & Epoch 提供了全新的元数据同步机制 +- 灵活的语义变更,在大多数场景下不需要重建模型也能够更灵活的应对业务变化 +- 明细索引,赋予了从 Valuable 的数据中下钻以及上卷的能力 +- 更灵活的 Runtime Join 从而更好的发挥预计算与实时计算各自的长处 + +本文主要是一篇介绍性的文章,希望能为您切入 Kylin5 提供些许帮助,接下来我们将提供更多关于 Kylin5 新特性设计原理的文章,期待大家一起加入到 Kylin5 的研发和试用。 diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2022-12-18-Introduction_of_Metadata/protocol-buffer/metadata.proto b/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2022-12-18-Introduction_of_Metadata/protocol-buffer/metadata.proto new file mode 100644 index 0000000000..899243e362 --- /dev/null +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2022-12-18-Introduction_of_Metadata/protocol-buffer/metadata.proto @@ -0,0 +1,643 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/************************************************************************************************** + * 文档说明 + * + * Kylin 5.0 的元数据和之前的版本存在比较大的额不兼容情况, 所以这里希望梳理元数据字段的含义, 方便用户和开发者进行 + * 理解和开发. (https://kylin.apache.org/5.0/blog/introduction_of_metadata_cn) + * + * 标签说明 + * - [TODO] 这个说明待补充 + * - [Trivial] 这个配置项不太重要, 不太影响产品行为, 可以不关心 + * + * 对应版本 : 5.0.0-alpha + * 更新时间 : 2023-01-16 + ************************************************************************************************/ +syntax = "proto3"; + +package org.apache.kylin.metadata; + +// ================================================================================================ +// ================================================================================================ +// ===================================== Part I =================================================== + +/** + * 包含了所有元数据的公共字段 + */ +message RootPersistentEntity { + /** + * 该元数据的全局唯一标示符 + */ + string uuid = 1; + int64 createTime = 2; + int64 lastModified = 3; + + // [Trivial] 标记这个元数据是由哪个版本的 Kylin 生成的, 可以用作元数据版本校验, 当前实际未使用 + string version = 4; + /** + * 用于避免元数据写入相互覆盖的自增字段 + */ + int64 mvcc = 5; + /** + * TODO + */ + bool isBroken = 6; +} + +message ProjectInstance { + RootPersistentEntity basicProp = 1; + string name = 2; + string owner = 3; + ProjectStatusEnum status = 4; + int64 createTimeUTC = 5; + string defaultDatabase = 6; + string description = 7; + + // [TODO] + string principal = 8; + + // [TODO] + string keytab = 9; + + // [Trivial] + MaintainModelType maintain_model_type = 10; + + /** + * 项目级别的配置(键值对), 可以覆盖全局的配置 + */ + map<string, string> override_kylin_properties = 11; + + /** + * 项目级别的 Segment 自动合并策略的配置 + */ + SegmentConfig segment_config = 12; + + enum ProjectStatusEnum { + // [TODO] + DISABLED = 0; + ENABLED = 1; + }; + + // [TODO] + enum MaintainModelType { + MANUAL_MAINTAIN = 0; + } +} + +message TableDesc { + RootPersistentEntity basicProp = 1; + + string name = 2; + repeated ColumnDesc columns = 3; + + SourceTypeEnum sourceType = 4; + + /** + * 用于区分一个表是不是视图, 外部表还是内部表 + */ + CatalogTableType tableType = 5; + + // [Trivial] for front end only + bool isTop = 6; + + // [Trivial] + string data_gen = 7; + + // [TODO] + string increment_loading = 8; + + string last_snapshot_path = 9; + + int64 last_snapshot_size = 10; + int64 snapshot_last_modified = 11; + + int32 query_hit_count = 12; + + string partition_column = 13; + + map<string, string> snapshot_partitions = 14; + map<string, string> snapshot_partitions_info = 15; + + int64 snapshot_total_rows = 16; + string snapshot_partition_col = 17; + string selected_snapshot_partition_col = 18; + string temp_snapshot_path = 19; + bool snapshot_has_broken = 20; + + string database = 21; + + bool transactional = 22; + + bool rangePartition = 23; + PartitionDesc partition_desc = 24; + + enum SourceTypeEnum { + ID_HIVE = 0; + ID_STREAMING = 1; // UNDER DEVELOPED + ID_SPARKSQL = 5; + ID_EXTERNAL = 7; + ID_JDBC = 8; + ID_SPARK = 9; + } + + /** + * 参考 org.apache.spark.sql.catalyst.catalog.CatalogTableType + */ + enum CatalogTableType { + EXTERNAL = 0; + MANAGED = 1; + VIEW = 2; + } +} + +// ================================================================================================ +// ================================================================================================ +// ===================================== Part II ================================================== + +/** + * Kylin 5 之前的 DataModel 和 CubeDesc 的合并 + */ +message NDataModel { + + RootPersistentEntity basicProp = 1; + + // model basic properties + string alias = 11; + string owner = 12; + + // 谁上次修改了模型级别配置项 + string configLastModifier = 13; + + // 上次修改了模型级别配置项的时刻 + int64 configLastModified = 14; + string description = 15; + + // model schema + string rootFactTableName = 20; + string rootFactTableAlias = 21; + + // 描述了事实表和维度表当前是如何连接的 + repeated JoinTableDesc joinTables = 22; + + // 打平表时是过滤数据的条件 + string filterCondition = 23; + PartitionDesc partitionDesc = 24; + + // all dimension and deleted column + repeated NamedColumn allNamedColumns = 25; + repeated Measure allMeasures = 26; + repeated ComputedColumnDesc computedColumnDescs = 27; + + // misc + DataCheckDesc dataCheckDesc = 40; + + // [Trivial] + string managementType = 41; + + // check https://kylin.apache.org/5.0/blog/introduction_of_metadata_cn#significant-change + int32 semanticVersion = 42; + + // [Trivial] + int32 storageType = 43; + + // [Trivial] + ModelType modelType = 44; + + // [Trivial] + int32 recommendationsCount = 45; + + // [Trivial] for front end only + string canvas = 46; + + // Broken reason because of schema change + BrokenReason brokenReason = 47; + + // [Trivial] + RealizationCapacity capacity = 48; + + // TODO + string multiPartitionDesc = 49; + + // TODO + string multiPartitionKeyMapping = 50; + + // [Trivial] for streaming feature + string fusionId = 51; + + // Some configuration of auto segment merge + SegmentConfig segmentConfig = 61; + + enum ModelType { + BATCH = 0; // Source is Hive only + STREAMING = 1; // Source is Kafka only, still under developing + HYBRID = 2; // Source is Kafka and Hive, still under developing + UNKNOWN = 3; + } + enum RealizationCapacity { + SMALL = 0; + MEDIUM = 1; + LARGE = 2; + } + enum BrokenReason { + SCHEMA = 0; + NULL = 1; + EVENT = 2; + } +} + +/** + * IndexPlan 是 Kylin5 中用来组织 Index/Layout 的元数据,它里面包含两个最重要的信息 RuleBasedIndex 和 Indexes, + * 前者用于管理聚合组索引,后者用于管理被物化的索引。 + */ +message IndexPlan { + string description = 1; + int64 retentionRange = 8; + + // [Trivial] + int32 engineType = 9; + repeated int64 autoMergeTimeRanges = 7; + + /** + * 静态 Index 剪枝策略, 例如聚合组规则 + */ + RuleBasedIndex ruleBasedIndex = 3; + + /** + * Indexes 属性用于管理用户 + * 1. 自定义明细索引 + * 2. 基础明细索引、基础聚合索引 + * 3. 以及用户通过扩展 Kylin 自动生成的索引, 例如 CubePlanner + */ + repeated IndexEntity indexes = 4; + + /** + * 全局字典相关的属性 + */ + repeated DictionaryDesc dictionaries = 10; + + /** + * 因为 Schema Change 而需要删除的索引 + */ + repeated IndexEntity toBeDeletedIndexes = 6; + + // TODO + map<int32, string> indexPlanOverrideIndexes = 10; + + // TODO + repeated int32 aggShardByColumns = 13; + + // TODO + map<int64, int32> layoutBucketNumMapping = 15; + + int64 nextAggregationIndexId = 11; + int64 nextTableIndexId = 12; + map<string, string> overrideProps = 5; +} + +/** + * Kylin 5 之前的 CubeInstance, 保存了索引和 Segment 相关数据的描述信息和统计数据 + */ +message DataFlow { + RealizationStatusEnum status = 1; + enum RealizationStatusEnum { + OFFLINE = 0; + ONLINE = 1; + BROKEN = 2; + } + + int32 cost = 2; + int32 queryHitCount = 3; + int64 lastQueryTime = 4; + repeated DataSegment segments = 6; +} + +// ================================================================================================ +// ================================================================================================ +// ===================================== Part III ================================================= + +message JoinTableDesc { + string table = 1; + TableKind kind = 2; + enum TableKind { + FACT = 0; + LOOKUP = 1; + } + string alias = 3; + JoinDesc join = 4; + ModelJoinRelationTypeEnum joinRelationTypeEnum = 6; + enum ModelJoinRelationTypeEnum { + MANY_TO_ONE = 0; + ONE_TO_ONE = 1; + ONE_TO_MANY = 2; + MANY_TO_MANY = 3; + } +} + +message NamedColumn { + int32 id = 1; + string name = 2; + string aliasDotColumn = 3; + + ColumnStatus status = 4; + enum ColumnStatus { + TOMB = 0; + EXIST = 1; + DIMENSION = 2; + } +} + +message Measure { + string name = 1; + int32 id = 2; + + bool tomb = 3; + FunctionDesc function = 4; + string column = 5; + string comment = 6; +} + +message ComputedColumnDesc { + // the table identity DB.TABLE (ignoring alias) in the model where the computed column be int64 to + // this field is more useful for frontend, for backend code, usage should be avoided + string tableIdentity = 1; + string tableAlias = 2; + string columnName = 3; // the new col name + string expression = 4; + string datatype = 5; + + string comment = 6; + string uuid = 7; +} + +message RuleBasedIndex { + repeated int32 dimensions = 2; // dimension id + repeated int32 measures = 3; //measure id + repeated int32 globalDimCap = 4; + repeated AggregationGroup aggregationGroups = 5; + repeated int32 layoutIdMapping = 6; // all of the layout id in agg group + + int32 parentForward = 7; + int64 indexStartId = 8; + int32 schedulerVersion = 11; + bool indexUpdateEnabled = 12; + + int64 lastModifiedTime = 9; +} + +message IndexEntity { + int64 id = 1; + repeated int32 dimensions = 2; + repeated int32 measures = 3; + repeated LayoutEntity layouts = 4; + int64 nextLayoutOffset = 5; +} + +message LayoutEntity { + int64 id = 1; + int64 updateTime = 10; + string name = 2; + string owner = 3; + + repeated int32 colOrder = 4; + repeated int32 shardByColumns = 6; + repeated int32 partitionByColumns = 7; + repeated int32 sortByColumns = 8; + + int32 storageType = 9; + bool isBase = 13; + string draftVersion = 14; + Range indexRange = 15; +} + +message DataCheckDesc { + CheckOptions checkOptions = 1; + enum CheckOptions { + PK_DUPLICATE = 0; + DATA_SKEW = 1; + NULL_OR_BLANK_VALUE = 2; + FORCE_ANALYSIS_LOOKUP = 3; + } + + int64 faultThreshold = 2; + int64 faultActions = 3; +} + +message DataSegment { + string id = 1; + string name = 2; + int64 createTimeUTC = 3; + + SegmentStatusEnum status = 4; + enum SegmentStatusEnum { + NEW = 0; + READY = 1; + WARNING = 2; + } + SegmentRange segmentRange = 5; + TimeRange timeRange = 6; + + map<string, DimensionRangeInfo> dimensionRangeInfoMap = 7; + map<string, string> dictionaries = 9; // table/column ==> dictionary resource path + map<string, string> snapshots = 10; // table name ==> snapshot resource path + int64 lastBuildTime = 11; // last segment incr build job + + // stats + int64 sourceCount = 12; + int64 sourceBytesSize = 13; + map<string, int64> columnSourceBytes = 14; + map<string, int64> oriSnapshotSize = 15; + int64 storageSize = 16; + int64 storageFileCount = 17; + map<string, string> additionalInfo = 18; + + + // resumable flag, don't cross building jobs + // worked only in HDFSMeteStore + bool isSnapshotReady = 20; + + // resumable flag, don't cross building jobs + // worked only in HDFSMeteStore + bool isDictReady = 21; + + // resumable flag, don't cross building jobs + // worked only in HDFSMeteStore + bool isFlatTableReady = 22; + + // resumable flag, don't cross building jobs + // worked only in HDFSMeteStore + bool isFactViewReady = 23; + + int64 maxBucketId = 25; + map<string, string> parameters = 8; +} + + +message DataFlowDetails { + string dataflowId = 1; + repeated DataLayout layouts = 2; +} + +message DataLayout { + int64 layoutId = 1; + int64 createTime = 11; + + string buildJobId = 2; + + int64 rows = 3; + int64 byteSize = 4; + int64 fileCount = 5; + int64 sourceRows = 6; + int64 sourceByteSize = 7; + // partition num may be diff with file num + int32 partitionNum = 8; + repeated string partitionValues = 9; + + bool isReady = 10; +} + + + +message JoinDesc { + string type = 1; + repeated string primaryKey = 2; + repeated string foreignKey = 3; + NonEquiJoinCondition nonEquiJoinCondition = 4; + string primaryTable = 5; + string foreignTable = 6; +} + +message NonEquiJoinCondition { + NonEquiJoinConditionType type = 1; + enum NonEquiJoinConditionType { + EXPRESSION = 0; // expression with other NonEquiJoinCondition as operands + COLUMN = 1; + LITERAL = 2; + } + string dataType = 2; + SqlKind op = 3; // kind of the operator + enum SqlKind { + OTHER = 0; + SELECT = 1; + JOIN = 2; + IDENTIFIER = 3; + // .etc + } + string opName = 4; // name of the operator + repeated NonEquiJoinCondition operands = 5; // nested operands + string value = 6; // literal or column identity at leaf node + string expr = 7; // set at runtime with model init +} + +message FunctionDesc { + string expression = 1; + repeated ParameterDesc parameters = 2; + string returnType = 3; + + map<string, string> configuration = 4; +} + +message ParameterDesc { + string type = 1; + string value = 2; +} + +message PartitionDesc { + string partitionDateColumn = 1; + string partitionDateFormat = 2; + PartitionType partitionType = 3; + string partitionConditionBuilderClz = 4; + + enum PartitionType { + APPEND = 0; + UPDATE_INSERT = 1; + } +} + +message ColumnDesc { + string id = 1; + string name = 2; + + string datatype = 3; + string comment = 4; + + string data_gen = 5; + string index = 6; + + /** + * 如果是一个普通列,那么这里是 null, 否则表示这是一个可计算列 + */ + string computedColumnExpr = 7; + string caseSensitiveName = 8; + + // [Trivial] 没有意义 + bool isPartitioned = 9; +} + +message SnapshotPartitionInfo { + int64 totalRows = 1; +} + +message StorageDescription { + string path = 1; +} + +message AggregationGroup { + repeated int32 includes = 1; + repeated int32 measures = 2; + SelectRule selectRule = 3; + Range indexRange = 4; +} + +enum Range { + BATCH = 0; + STREAMING = 1; + HYBRID = 2; + EMPTY = 3; +} + +message SelectRule { + repeated int32 hierarchyDims = 1; + repeated int32 mandatoryDims = 2; + repeated int32 jointDims = 3; + repeated int32 dimCap = 4; +} + +message DictionaryDesc { + int32 id = 1; + int32 reuseId = 2; + string builderClass = 3; +} + +message DimensionRangeInfo { + int64 min = 1; + int64 max = 2; +} + +message SegmentRange { + int64 start = 1; + int64 end = 2; +} + +message TimeRange { + int64 start = 1; + int64 end = 2; +} + +message SegmentConfig { +} \ No newline at end of file diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2023_01_16-Introduction_of_Metastore/diagram/ER-diagram/metadata_store.png b/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2023_01_16-Introduction_of_Metastore/diagram/ER-diagram/metadata_store.png new file mode 100644 index 0000000000..4110d34472 Binary files /dev/null and b/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2023_01_16-Introduction_of_Metastore/diagram/ER-diagram/metadata_store.png differ diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2023_01_16-Introduction_of_Metastore/diagram/ER-diagram/metadata_store.puml b/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2023_01_16-Introduction_of_Metastore/diagram/ER-diagram/metadata_store.puml new file mode 100644 index 0000000000..909f161d08 --- /dev/null +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2023_01_16-Introduction_of_Metastore/diagram/ER-diagram/metadata_store.puml @@ -0,0 +1,44 @@ +@startuml + +' avoid problems with angled crows feet +skinparam linetype ortho + +entity "Epoch" as epoch { + EPOCH_ID : int // 标识第几次纪元, 每次续约或者抢占都会自增, 从 0 开始 + * EPOCH_TARGET : varchar // <UNIQUE KEY> 竞争单元 + -- + CURRENT_EPOCH_OWNER : varchar // Kylin 进程地址, 通常是 IP:PORT|START_TIME_TS + LAST_EPOCH_RENEW_TIME : bigint // EPOCH 需要定期续约, 可以通过这个字段确定 Epoch 是否过期 + SERVER_MODE : varchar // Kylin 节点角色 + MAINTENANCE_MODE_REASON : varchar // 不重要 + MVCC : bigint // 避免写冲突 + + // 竞争单元, 通常是项目单元(元数据变更或者提交作业), + // 除此以外还有一个全局的 Epoch(执行全局定时任务(垃圾清理、索引优化),更新 user、acl 等元数据) +} + +entity "AuditLog" as audit { + * ID : bigint <auto_increment primary key> + ---- + AUDIT_LOG_TABLE_KEY : varchar + AUDIT_LOG_TABLE_CONTENT : longblob + AUDIT_LOG_TABLE_TS : bigint + AUDIT_LOG_TABLE_MVCC : bigint + UNIT_ID : varchar // 事务标识 + OPERATOR : varchar // 操作者账号 + INSTANCE : varchar // Kylin 的节点和端口 +} + + +entity "Metadata" as meta { + * META_TABLE_KEY : varchar <primary key> // 元数据的键, 例如: learn_kylin/table/SSB.PART.json + ---- + META_TABLE_CONTENT : longblob // 元数据的值(内容), 内容为 Json 格式, 和 RootPersistentEntity 对应 + META_TABLE_TS : bigint // 这行数据最后的修改时间 + META_TABLE_MVCC : bigint // 这行数据被修改的次数,mvcc 就是 Multi-Version Concurrency Control +} + + +meta }|-- audit : META_TABLE_KEY-AUDIT_LOG_TABLE_KEY + +@enduml \ No newline at end of file diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2023_01_16-Introduction_of_Metastore/diagram/activity_diagram/write_and_read_persistent_entity.puml b/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2023_01_16-Introduction_of_Metastore/diagram/activity_diagram/write_and_read_persistent_entity.puml new file mode 100644 index 0000000000..ce38478968 --- /dev/null +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2023_01_16-Introduction_of_Metastore/diagram/activity_diagram/write_and_read_persistent_entity.puml @@ -0,0 +1,98 @@ +@startuml + +start + +if (alreadyInTransaction?) then (yes) + :call process() of inner transaction; + end +endif + +repeat + :Update Transaction Metrics; + :UnitOfWorkParams.getProcessor().preprocess(); + + :UnitOfWork#startTransaction; + partition #lightyellow "startTransaction" { + if (EpochNotOwnedByCurrentProcess?) then (no) + :throw Exception; + end + endif + : checkThreadLock; + : lockThreadLock; + if(isSandboxMode) then (yes) + : prepare thread kylin config; + : prepare thread ResourceStore(ThreadViewResourceStore); + endif + } + + :UnitOfWorkParams.getProcessor().process(); + :process(real business logic)// change is stored in ThreadViewResourceStore; + partition #azure "business logic" { + : copy new PersistentEntity for write; + : update on new PersistentEntity; + : create XXXManager; + : call XXXManager#save(); + partition "XXXManager" { + : some check; + : CachedCrudAssist#save(); + : ResourceStore#checkAndPutResource(); + } + } + + + :UnitOfWork#endTransaction; + partition #powderblue "endTransaction" { + : remove thread kylin config; + if(isSandboxMode) then (yes) + : Fetch change from thread ResourceStore; + endif + + :flushSandboxMeta; + :write_AuditLogStore; + :replay audit log into resource store; + + } + + +repeat while (Transaction failed and not hit max retry times?) is (retry) not (Succeed or not failed) + ->//return a object//; +stop + +'autonumber 1.0 +'doInTransactionWithRetry -> isAlreadyInTransaction +' +'isAlreadyInTransaction --> doInTransactionWithRetry +' +'doInTransactionWithRetry -> updateMetrics +' +'loop "Retry-transaction" [123] +' autonumber 2.0 +' updateMetrics -> preProcess +' +' group doTransaction [] +' autonumber 3.0 +' preProcess -> startTransaction +' +' group startTransaction [hello] +' startTransaction -> checkEpoch +' +' checkEpoch -> fetchLock +' +' fetchLock -> lock +' end +' +' lock -> process : 业务逻辑 +' +' +' process -> flushSandboxMeta +' +' group endTransaction [] +' autonumber 4.0 +' flushSandboxMeta -> write_AuditLogStore +' +' write_AuditLogStore -> write_Metastore +' end +' +'end + +@enduml \ No newline at end of file diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2023_01_16-Introduction_of_Metastore/diagram/activity_diagram/write_and_read_persistent_entity_cn.png b/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2023_01_16-Introduction_of_Metastore/diagram/activity_diagram/write_and_read_persistent_entity_cn.png new file mode 100644 index 0000000000..170f84cfaf Binary files /dev/null and b/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2023_01_16-Introduction_of_Metastore/diagram/activity_diagram/write_and_read_persistent_entity_cn.png differ diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2023_01_16-Introduction_of_Metastore/diagram/activity_diagram/write_and_read_persistent_entity_cn.puml b/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2023_01_16-Introduction_of_Metastore/diagram/activity_diagram/write_and_read_persistent_entity_cn.puml new file mode 100644 index 0000000000..a5d1f7e435 --- /dev/null +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2023_01_16-Introduction_of_Metastore/diagram/activity_diagram/write_and_read_persistent_entity_cn.puml @@ -0,0 +1,68 @@ +@startuml + +start + +note right + 这个是 Kylin 5.0 **元数据事务** 的提交流程分析 + ====== + 1. 元数据事务的相关代码在 UnitOfWork, 本活动图是对代码的说明; + 2. Epoch 是全局级别的, 项目粒度的元数据写锁, 用于确保同一时刻只有一个进程会修改指定项目下的元数据; + 3. 为了避免获得 Epoch 的进程多线程同时变更元数据, 所以获得 Epoch 的进程, 还需要进行一个进程内的写锁的锁定, 以确保获得 Epoch 的进程对元数据的变更, 是串行的; + 4. 调用 endTransaction 方法前, 元数据变更会发生在一个沙盒模式的 ResourceStore; + 5. 在 endTransaction 方法, 会将元数据变更持久化到 Metastore(RDBMS) 和 AuditLogStore(RDBMS), 这里使用 RDBMS 的事务保证元数据操作的一致性; + 6. 其他 Kylin 进程会定期获取 AuditLogStore 的变更, 重放到自己的 ResourceStore. +end note + +if (是否已经在事务?) then (yes) + :调用"被包含"的子事务的 process 方法并且退出子事务; + end +endif + +repeat + :更新元数据事务相关的 Metrics; + :调用 UnitOfWorkParams.getProcessor().preprocess(), 目前没有太多作用; + + partition #lightyellow "startTransaction" { + + if (当前进程是否拥有 Epoch?) then (no) + :抛出异常并且结束事务; + end + endif + : 获取进程内写锁; + : 对进程内写锁进行锁定; + if(是否启用沙盒模式?) then (yes) + : 创建 KylinConfig 的快照并且进行设置到 ThreadLocal; + : 创建 ThreadViewResourceStore, 以保证事务提交前\n事务内的元数据变更, 不会对外部可见; + endif + } + floating note #pink: 开始元数据事务的准备工作, \n相关代码在 UnitOfWork#startTransaction + + partition #azure "process 方法包含的元数据修改逻辑(业务逻辑)" { + : 获取 startTransaction 准备的 KylinConfig; + : 创建 XXXManager 和 PersistentEntity; + : 使用 XXXManager 对变更后的 PersistentEntity 进行保存; + floating note #pink: 调用 UnitOfWorkParams.getProcessor().process()\n, process 方法由 Service 层传递, process 方法包含业务逻辑,\n在这一阶段元数据变更会发生在沙盒内(也就是 ThreadViewResourceStore) + partition "XXXManager" { + : XXXManager 对变更后的 PersistentEntity 进行约束性检查; + : XXXManager 调用 CachedCrudAssist#save() 写入 Cache; + : CachedCrudAssist 调用 ResourceStore#checkAndPutResource() \n写入 ThreadViewResourceStore 的 overlay; + } + } + + partition #powderblue "endTransaction" { + floating note #pink: 调用 UnitOfWork#endTransaction 来将\n ThreadViewResourceStore 的元数据变更提交; + : 清理 ThreadLocal 级别的 KylinConfig; + : 再次检查当前进程是否持有 Epoch; + if(是否启用沙盒模式?) then (yes) + : 从 ThreadViewResourceStore 的 overlay 收集元数据变更, 保存到 UnitMessages; + : 在一个 RDBMS 事务内, 提交 UnitMessages 的变更到\n MetadataStore 和 AuditLogStore 对应的表; + : 通过 MessageSynchronization 将 UnitMessages 缓存的元数据变更\n重放到原先的 ResourceStore(此时对外部可见); + endif + :更新元数据事务的耗时指标; + } + +repeat while (事务失败, 并且事务失败次数尚未超出重试最大次数?) is (是, 则触发事务重试) not (否, 那么事务成功, 或者事务失败次数超出阈值) + ->\n//返回 Pair<Boolean, T>, 其中 Boolean 标示事务是否成功, T 是业务逻辑(也就是 process方法)的返回值 //; +stop + +@enduml \ No newline at end of file diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2023_01_16-Introduction_of_Metastore/diagram/class_diagram/metastore_resource_store.png b/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2023_01_16-Introduction_of_Metastore/diagram/class_diagram/metastore_resource_store.png new file mode 100644 index 0000000000..03ece7d787 Binary files /dev/null and b/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2023_01_16-Introduction_of_Metastore/diagram/class_diagram/metastore_resource_store.png differ diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2023_01_16-Introduction_of_Metastore/diagram/class_diagram/metastore_resource_store.puml b/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2023_01_16-Introduction_of_Metastore/diagram/class_diagram/metastore_resource_store.puml new file mode 100644 index 0000000000..b2712369ff --- /dev/null +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2023_01_16-Introduction_of_Metastore/diagram/class_diagram/metastore_resource_store.puml @@ -0,0 +1,126 @@ +@startuml + +namespace business_layer { + class DataModelManager #pink { + 'CRUD operation of DataModel // first kind method; + 'complex business logic of DataModel // second kind method; + + private NDataModel saveDataModelDesc(NDataModel dataModelDesc); // metadata write path, level 1 + } + + class CachedCrudAssist<T extends RootPersistentEntity> #pink { + protected Cache<String, T extends RootPersistentEntity> cache; // cache of RootPersistentEntity + ResourceStore resourceStore; + + public T save(T entity); // metadata write path, level 2 + } +} + + +namespace resource_store { + + + class RawResource { + private String resPath; + private ByteSource byteSource; + private long timestamp; + private long mvcc; + } + + class VersionedRawResource { + RawResource resource; + Integer mvcc; + } + + abstract class ResourceStore { + static Cache<KylinConfig, ResourceStore> META_CACHE; + volatile ConcurrentSkipListMap<String, VersionedRawResource> data; // cache of byte array + + public final <T extends RootPersistentEntity> void checkAndPutResource(String resPath, T obj, + Serializer<T> serializer); // metadata write path, level 3 + } + + class InMemResourceStore + + class ThreadViewResourceStore { + // ThreadViewResourceStore like a sandbox, update on ThreadViewResourceStore won't impact outside.\n ThreadViewResourceStore is used in transaction(UnitOfContext) only. */ + + InMemResourceStore overlay; // store local change of an uncommitted transaction + InMemResourceStore underlying; // real ResourceStore + } +} + + +namespace metastore { + abstract class MetadataStore { + void putResource(RawResource res, String unitPath, long epochId); // metadata write path, level 4 + } + + + class JDBCMetadataStore { + DataSourceTransactionManager transactionManager; + JdbcTemplate jdbcTemplate; + RowMapper<RawResource> RAW_RESOURCE_ROW_MAPPER; + } + + class HDFSMetadataStore + class FileMetadataStore + + class JdbcUtil { + + } + + abstract class EpochStore { + + } +} + + +namespace sync { + abstract class AuditLogStore { + void save(UnitMessages unitMessages); // metadata write path, level 5 + } + + class JDBCAuditLogStore +} + + +'---------------------- Class Relation(extends) --------------------- + +resource_store.InMemResourceStore --|> resource_store.ResourceStore : extends + +resource_store.ThreadViewResourceStore --|> resource_store.ResourceStore : extends + +resource_store.VersionedRawResource --|> resource_store.RawResource : extends + + +metastore.JDBCMetadataStore --|> metastore.MetadataStore : extends +metastore.HDFSMetadataStore --|> metastore.MetadataStore : extends +metastore.FileMetadataStore --|> metastore.MetadataStore : extends + +sync.JDBCAuditLogStore --|> sync.AuditLogStore : extends + +'---------------------- Class Relation(contains) --------------------- + + +business_layer.DataModelManager *-- "business_layer.CachedCrudAssist" : contains + +"business_layer.CachedCrudAssist" *-- resource_store.ResourceStore : contains + +resource_store.ResourceStore *-- metastore.MetadataStore : contains + +metastore.MetadataStore *-- sync.AuditLogStore : contains + +metastore.MetadataStore *-- metastore.EpochStore : contains + +resource_store.ThreadViewResourceStore *-- resource_store.InMemResourceStore : contains + + + +'---------------------- Class Relation(relate) --------------------- + +resource_store.ResourceStore --> resource_store.VersionedRawResource : store + + + +@enduml \ No newline at end of file diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2023_01_16-Introduction_of_Metastore/index.md b/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2023_01_16-Introduction_of_Metastore/index.md new file mode 100644 index 0000000000..0b65b51536 --- /dev/null +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-blog/2023_01_16-Introduction_of_Metastore/index.md @@ -0,0 +1,239 @@ +--- +title: Metastore 介绍 +slug: introduction_of_metastore_cn +authors: [xxyu] +tags: [metastore, kylin5] +hide_table_of_contents: false +date: 2023-01-16T10:00 +--- + +:::tip Before your read +**Target Audience** +- 对 Kylin 5.0 元数据存储, 元数据缓存, 以及节点间元数据同步机制感兴趣的用户和开发者. +- 在二次开发过程中, 想了解对 Kylin 5.0 进行元数据读写操作的最佳实践和注意事项的开发者. +- 想对 Kylin 5.0 的元数据进行升级改造的开发者. + + +**What will you learn** +- 了解在 Kylin 5 如何进行读写元数据操作 + +💬 Kylin 5 的开发者需要了解元数据读写的逻辑的实现和技术细节 +::: + +<!--truncate--> + + +### Target Audience + +这篇文档是为有以下需求的用户和开发者而准备的: +1. 对 Kylin 5.0 元数据存储, 元数据缓存, 以及节点间元数据同步机制感兴趣的用户和开发者. +2. 在二次开发过程中, 想了解对 Kylin 5.0 进行元数据读写操作的最佳实践和注意事项的开发者. +3. 想对 Kylin 5.0 的元数据进行升级改造的开发者. + +### Terminology + +#### Core Class and Interface + +| Class | Comment | +|-------------------------|----------------------------------------------------| +| ResourceStore | 用于管理内存中对元数据的操作 | +| InMemResourceStore | ResourceStore 的实现类, 用于绝大部分情况 | +| ThreadViewResourceStore | ResourceStore 的实现类, 作为一个沙盒式的 ResourceStore, 在事务中使用 | +| MetadataStore | 用于管理元数据持久化的操作 | +| AuditLogStore | 用于节点间元数据同步, 以及诊断元数据异常情况 | +| Epoch | 用于保证同时只有一个进程对指定项目下的元数据进行修改操作, 或者提交作业 | +| EpochStore | 用于持久化 Epoch | + + +### Transaction of metadata CRUD + +#### Why Kylin 5.0 need transaction(of metadata) ? +因为用户操作可能同时操作多个元数据, 这些元数据变更必须保持一致性, 要么同时变更成功要么同时变更失败, 不允许出现中间状态. +例如, 如果用户修改 DataModel 的维度和度量, IndexPlan 也需要随之同时变更, 两者必须保持一致性. + +#### How transaction was implemented? +检查 [元数据更新流程图](#metadata_write) + +#### How should I write a piece of meta? + +1. 使用注解 Transaction + + 使用 `@org.apache.kylin.rest.aspect.Transaction` 对你的业务方法进行注解, 这个在你的方法比较简短(轻量)的情况比较推荐 + ```java + class JobService{ + + @Transaction(project = 0) + public ExecutableResponse manageJob(String project, ExecutableResponse job, String action) throws IOException { + Preconditions.checkNotNull(project); + Preconditions.checkNotNull(job); + Preconditions.checkArgument(!StringUtils.isBlank(action)); + + if (JobActionEnum.DISCARD == JobActionEnum.valueOf(action)) { + return job; + } + + updateJobStatus(job.getId(), project, action); + return getJobInstance(job.getId()); + } + } + ``` +2. 使用 EnhancedUnitOfWork + + 使用 `EnhancedUnitOfWork.doInTransactionWithCheckAndRetry` 来包含你的业务代码(元数据修改代码) + ```java + class SomeService { + public void renameDataModel() { + // some codes not in transaction + EnhancedUnitOfWork.doInTransactionWithCheckAndRetry(() -> { + // 1. Get XXXManager, such as NDataModelManager + KylinConfig config = KylinConfig.getInstanceFromEnv(); // thread local KylinConfig was created in startTransaction + NDataModelManager manager = config.getManager(project, NDataModelManager.class); + // 2. Get RootPersistentEntity by XXXManager and Update XXX + NDataModel nDataModel = modelManager.getDataModelDesc(modelId); + checkAliasExist(modelId, newAlias, project); + nDataModel.setAlias(newAlias); + NDataModel modelUpdate = modelManager.copyForWrite(nDataModel); + // 3. Call updateXXX method of XXXManager + modelManager.updateDataModelDesc(modelUpdate); + }, project); + // some more codes not in transaction + } + } + ``` + +:::info 元数据操作须知 +1. KylinConfig 和 XXXManager(例如:NDataModelManager) 需要在事务内获取, + 原因在于事务开启时准备了一个事务专用的单独的 KylinConfig, 单独的 KylinConfig 绑定了事务需要的 ResourceStore +2. 开启多线程修改元数据需要注意, 原因在于新的线程并不能获取事务开启时准备的 KylinConfig, 而是全局的 KylinConfig +3. 两个事务写的时候别复用同一个对象, 以避免元数据更新时, MVCC 检查失败 +::: + +### Epoch + +1. 什么是 Epoch? + + 竞争单元: 由于同时存在多个 leader,多个 leader 会竞争成为单元的 owner + + - 全局竞争单元:执行全局定时任务(垃圾清理、索引优化),更新 user、acl 等元数据; + - 项目竞争单元:项目级定时任务,项目内元数据变更; + + Epoch (纪元):每个竞争单元发生一次占有的变化,就称为一个 epoch。<br></br>每个竞争单元都会独立地在元数据中记录自己的纪元相关属性。 + +2. 什么样的进程可以获取 Epoch? + + Job 和 All, Query 不可以获得 Epoch, Query 节点被设计为不需要提交任务或者是修改元数据. + +3. Epoch 如何管理? + + all 节点与 job 节点在刚启动时,就会启动定时任务,也就是心跳检测时间: kylin.server.leader-race.heart-beat-interval 参数来控制时间间隔,不断地去竞争 epoch。 + 如果当前项目的 epoch owner 不合法,那么就会进行抢占,以下情况可以成功抢占: + - epoch owner 为空(节点停止时,会清空 current_epoch_owner) + - 当前 epoch owner 已经过期,过期判断逻辑:(当前时间 - last_epoch_renew_time) > kylin.server.leader-race.heart-beat-timeout(心跳检测超时时间) + +4. 疑问 + + **如何防止脑裂**? + + 脑裂:两个节点都认为自己是 epoch owner。<br></br> + 在开启事务前、写入数据库前都会进行 epoch owner 检查,只有正确的 owner 才能成功写入元数据。 + + **节点下线** + + Kylin 节点停止时,会将当前节点所有子进程都杀死。最终是调用 sbin/ kill-process-tree.sh 脚本,对每个子进程,先 kill,kill 后还没停止的话,等 5s,还没停止的话,再执行 kill -9 pid。 + 节点停止时,真个 kill 过程,超时时间是 1 分钟,会打印相关日志。<br></br> +``` + 成功: Destroy process {pid} of job {jobId} SUCCEED. + 失败: Destroy process {pid} of job {jobId} FAILED. + 超时: Destroy process {pid} of job {jobId} TIMEOUT exceed 60s. +``` + + **负载不均衡** + + 启动时:先启动的节点会抢占所有 epoch;<br></br> + 节点下线:如果某个节点下线,它占有的所有 epoch 会释放,可能会被一个节点抢占到;<br></br> + 可以通过 API 可以更新 epoch owner。<br></br> + +5. 相关类 + + - EpochOrchestrator + - EpochOrchestrator.EpochChecker + - EpochOrchestrator.EpochRenewer + - EpochManager + - EpochChangedListener + +6. 相关事件 + - ProjectControlledNotifier + +7. 相关配置 + + - kylin.server.leader-race.enabled=false : Job 多活开关,默认开启,如果关闭,那么 epoch 将会设置为永不过期。 + - kylin.server.leader-race.heart-beat-timeout=60 :代表心跳检测的超时时间,默认值 60 秒。当一个 Job 节点超过 60 秒没有发出心跳,则代表该节点不可用,该节点持有的项目Epoch就会被别的节点抢占。 + - kylin.server.leader-race.heart-beat-interval=30 :代表发起心跳的时间间隔,默认值 30 秒,代表每个任务节点每隔 30 秒发送一次心跳。 + +### How Kylin load ResourceStore when started + +#### Query Node +关键日志: `"start restore, current max_id is {}"` +1. Query 节点会先从 hdfs 上读取备份的最新元数据,加载到内存中 +2. 再根据 _image 中记录的 audit log offset,从 offset 开始,将数据库中的 audit log 读取到内存中,一次最多读 1000 条,目前这个数值不可配置。 +3. 在此过程中,all/job 节点还会同时写元数据,写 audit log,所以还会启动一个定时任务,每隔几秒( kylin.metadata.audit-log.catchup-interval ) + 去 catch up audit log,保证 query 节点与 all 节点元数据保持一致。 + +#### Leader(All/Job) +关键日志 :`"current maxId is {}"` + +1. Leader 节点在启动时,先从元数据库恢复元数据; +2. 再开启定时任务,catch up 最新的元数据; +3. 如果发现是自己写的 audit log,则跳过这条记录; + +与 Query 节点的主要不同点在于:Query 节点是从 HDFS 备份的元数据中先进行一次恢复,再从 audit log 中获取新的元数据, +此时可能要 catchup 的数据量较大, 所以在启动定时任务之前,先恢复到当时最大的 audit log,之后再开启定时任务。 +而 Leader 节点本身就从元数据库恢复元数据,所以中间相差的记录数很少,就直接开启定时任务执行 catchup。 + +#### Query 节点加载元数据的方式为什么和 job/all 不同 +> 为什么 query 节点需要通过 hdfs+audit log 来恢复元数据,而不是像 Leader 节点那样,通过元数据表+audit log 恢复? + +背景: 在从元数据库恢复元数据时,会同时获取 audit log 的最后一条记录,也就是 max id,获取到当时的 max id,之后再以此为起点,replay 之后的元数据。 + +问题: 如果已经有一个 Leader 节点 A,此时启动 Leader 节点 B,如果 B 节点恢复了元数据,但还没有获取到 audit log 的 max id, +此时 A 节点仍然往里面写了数据,那么此时 B 节点拿到的 max id 是大于本来要拿的 max id,导致中间会少一部分元数据。 + +解决方案: 上面整个过程是在同一个事务中,使用的事务隔离级别是 serializable,在此过程中会加上读写锁,恢复元数据时, +其他节点不能写入元数据。生产环境中,往往是 Query 节点多,Leader 节点少,如果 Query 节点也采用 元数据表+audit log 恢复元数据, +Query 节点频繁启动,不能写入元数据的时间就会变长,影响性能。[源代码在这里](https://github.com/apache/kylin/blob/edab8698b6a9770ddc4cd00d9788d718d032b5e8/src/core-common/src/main/java/org/apache/kylin/common/persistence/metadata/JdbcMetadataStore.java#L305). + +#### MetadataStore, AuditLogStore and EpochStore in RDBMS + +[DDL of MetadataStore, AuditLogStore and EpochStore](https://github.com/apache/kylin/blob/edab8698b6a9770ddc4cd00d9788d718d032b5e8/src/core-common/src/main/resources/metadata-jdbc-mysql.properties) + +### Metadata Sync +#### How do meta was synced in Kylin Cluster? +对于指定项目, Kylin 节点要获取 Epoch 才能对元数据进行写操作, 所以对于这个项目下面元数据, 没有获取 Epoch 的 Kylin 节点 +将作为 Follower 通过 AuditLogReplayWorker 获取元数据更新. + +Follower 同步元数据变更,通过两个方式,代码在 AuditLogReplayWorker +1. 第一个是 Follower 自己定期调度同步任务,默认间隔是 5s; +2. 第二个方式是元数据变更的节点发送 AuditLogBroadcastEventNotifier 告知所有 Follower, Follower 主动 replay + +按照设计,Follower 元数据的 delay 在 1-2s 左右(被动广播同步),最多 5s(主动定期同步). + +### Diagram + +#### <span id="metadata_write">Diagram of write a piece of meta </span> +1. 元数据事务的相关代码在 `UnitOfWork`, 本活动图是对代码的说明; +2. `Epoch` 是全局级别的, 项目粒度的元数据写锁, 用于确保同一时刻只有一个进程会修改指定项目下的元数据; +3. 为了避免多线程同时变更元数据, 所以获得 Epoch 的进程, 还需要进行一个进程内的写锁的锁定, 以确保获得 `Epoch` 的进程对元数据的变更, 是串行的; +4. 调用 `endTransaction` 方法前, 元数据变更会发生在一个沙盒模式的 `ResourceStore`; +5. 在 `endTransaction` 方法, 会将元数据变更持久化到 `Metastore`(RDBMS) 和 `AuditLogStore`(RDBMS), 这里使用 RDBMS 的事务保证元数据操作的一致性; +6. 关于事物的异常和回滚, 在 `process` 方法发生的异常因为元数据变更是发生在沙盒的, 所以直接舍弃沙盒即可; +在 `endTransaction` 发生的异常, 由 RDBMS 事务保证对 Metastore 和 AudiLogStore 操作的原子性; +7. 其他 Kylin 进程会定期获取 `AuditLogStore` 的变更, 重放到自己的 `ResourceStore`, 来保持节点间的元数据的一致性. + + + +#### <span id="class_metastore">Class Diagram of Metastore</span> + + + +#### Metadata Store Schema + \ No newline at end of file diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-blog/authors.yml b/website/i18n/zh-Hans/docusaurus-plugin-content-blog/authors.yml new file mode 100644 index 0000000000..7edc397f21 --- /dev/null +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-blog/authors.yml @@ -0,0 +1,17 @@ +xxyu: + name: Xiaoxiang Yu + title: PMC member and Release Manager of Apache Kylin + url: https://github.com/hit-lacus + image_url: https://github.com/hit-lacus.png + +pfzhan: + name: Pengfei Zhan + title: Dev of Kyligence Inc + url: https://github.com/pfzhan + image_url: https://github.com/pfzhan.png + +jlfsdtc: + name: Jiang Longfei + title: Dev of Kyligence Inc + url: https://github.com/jlfsdtc + image_url: https://github.com/jlfsdtc.png \ No newline at end of file diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-blog/options.json b/website/i18n/zh-Hans/docusaurus-plugin-content-blog/options.json new file mode 100644 index 0000000000..48dead2abd --- /dev/null +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-blog/options.json @@ -0,0 +1,14 @@ +{ + "title": { + "message": "博客", + "description": "The title for the blog used in SEO" + }, + "description": { + "message": "Kylin 5.0 技术博客", + "description": "The description for the blog used in SEO" + }, + "sidebar.title": { + "message": "技术博客", + "description": "The label for the left sidebar" + } +} diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current.json b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current.json new file mode 100644 index 0000000000..bb26665207 --- /dev/null +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current.json @@ -0,0 +1,154 @@ +{ + "version.label": { + "message": "5.0.0", + "description": "The label for version current" + }, + "sidebar.DocumentSideBar.category.Quick Start": { + "message": "快速启动", + "description": "The label for category Quick Start in sidebar DocumentSideBar" + }, + "sidebar.DocumentSideBar.category.Deployment": { + "message": "部署", + "description": "The label for category Deployment in sidebar DocumentSideBar" + }, + "sidebar.DocumentSideBar.category.Prerequisite": { + "message": "环境准备", + "description": "The label for category Prerequisite in sidebar DocumentSideBar" + }, + "sidebar.DocumentSideBar.category.Configuration": { + "message": "配置", + "description": "The label for category Configuration in sidebar DocumentSideBar" + }, + "sidebar.DocumentSideBar.category.Datasource": { + "message": "数据源", + "description": "The label for category Datasource in sidebar DocumentSideBar" + }, + "sidebar.DocumentSideBar.category.Internal Table": { + "message": "内表", + "description": "The label for category Internal Table in sidebar DocumentSideBar" + }, + "sidebar.DocumentSideBar.category.Model": { + "message": "模型", + "description": "The label for category Model in sidebar DocumentSideBar" + }, + "sidebar.DocumentSideBar.category.Manual Modeling": { + "message": "手工建模", + "description": "The label for category Manual Modeling in sidebar DocumentSideBar" + }, + "sidebar.DocumentSideBar.category.Recommendation": { + "message": "智能建模", + "description": "The label for category Recommendation in sidebar DocumentSideBar" + }, + "sidebar.DocumentSideBar.category.Management": { + "message": "模型管理", + "description": "The label for category Management in sidebar DocumentSideBar" + }, + "sidebar.DocumentSideBar.category.Advanced Features": { + "message": "高级功能", + "description": "The label for category Advanced Features in sidebar DocumentSideBar" + }, + "sidebar.DocumentSideBar.category.Measures": { + "message": "度量", + "description": "The label for category Measures in sidebar DocumentSideBar" + }, + "sidebar.DocumentSideBar.category.Streaming": { + "message": "实时功能", + "description": "The label for category Streaming in sidebar DocumentSideBar" + }, + "sidebar.DocumentSideBar.category.Query": { + "message": "查询", + "description": "The label for category Query in sidebar DocumentSideBar" + }, + "sidebar.DocumentSideBar.category.SQL Specification": { + "message": "SQL 规范", + "description": "The label for category SQL Specification in sidebar DocumentSideBar" + }, + "sidebar.DocumentSideBar.category.Advanced Functions": { + "message": "高级函数", + "description": "The label for category Advanced Functions in sidebar DocumentSideBar" + }, + "sidebar.DocumentSideBar.category.Query Tuning": { + "message": "查询优化", + "description": "The label for category Query Tuning in sidebar DocumentSideBar" + }, + "sidebar.DocumentSideBar.category.Query History": { + "message": "查询历史", + "description": "The label for category Query History in sidebar DocumentSideBar" + }, + "sidebar.DocumentSideBar.category.Maintenance Guide": { + "message": "维护指南", + "description": "The label for category Maintenance Guide in sidebar DocumentSideBar" + }, + "sidebar.DocumentSideBar.category.Project Managing": { + "message": "项目管理", + "description": "The label for category Project Managing in sidebar DocumentSideBar" + }, + "sidebar.DocumentSideBar.category.Access Control": { + "message": "访问控制", + "description": "The label for category Access Control in sidebar DocumentSideBar" + }, + "sidebar.DocumentSideBar.category.Data Access Control": { + "message": "数据访问控制", + "description": "The label for category Data Access Control in sidebar DocumentSideBar" + }, + "sidebar.DocumentSideBar.category.System Operation": { + "message": "系统管理", + "description": "The label for category System Operation in sidebar DocumentSideBar" + }, + "sidebar.DocumentSideBar.category.Diagnosis": { + "message": "诊断", + "description": "The label for category Diagnosis in sidebar DocumentSideBar" + }, + "sidebar.DocumentSideBar.category.CLI Operation Tool": { + "message": "命令行工具", + "description": "The label for category CLI Operation Tool in sidebar DocumentSideBar" + }, + "sidebar.DocumentSideBar.category.Job Monitoring": { + "message": "任务监控", + "description": "The label for category Job Monitoring in sidebar DocumentSideBar" + }, + "sidebar.DocumentSideBar.category.System Monitoring": { + "message": "系统监控", + "description": "The label for category System Monitoring in sidebar DocumentSideBar" + }, + "sidebar.DocumentSideBar.category.InfluxDB": { + "message": "InfluxDB", + "description": "The label for category InfluxDB in sidebar DocumentSideBar" + }, + "sidebar.DocumentSideBar.category.Logs": { + "message": "日志", + "description": "The label for category Logs in sidebar DocumentSideBar" + }, + "sidebar.DocumentSideBar.category.Integration": { + "message": "BI 集成", + "description": "The label for category Integration in sidebar DocumentSideBar" + }, + "sidebar.DocumentSideBar.category.Rest API": { + "message": "Rest API", + "description": "The label for category Rest API in sidebar DocumentSideBar" + }, + "sidebar.DocumentSideBar.category.Model Management": { + "message": "模型管理", + "description": "The label for category Model Management in sidebar DocumentSideBar" + }, + "sidebar.DocumentSideBar.category.ACL Management API": { + "message": "权限管理 API", + "description": "The label for category ACL Management API in sidebar DocumentSideBar" + }, + "sidebar.DevelopmentSideBar.category.Development Guide": { + "message": "开发指南", + "description": "The label for category Development Guide in sidebar DevelopmentSideBar" + }, + "sidebar.CommunitySideBar.doc.Community": { + "message": "社区", + "description": "The label for the doc item Community in sidebar CommunitySideBar, linking to the doc community" + }, + "sidebar.CommunitySideBar.doc.Use Cases": { + "message": "使用案例", + "description": "The label for the doc item Use Cases in sidebar CommunitySideBar, linking to the doc poweredby" + }, + "sidebar.DownloadSideBar.doc.Download": { + "message": "下载", + "description": "The label for the doc item Download in sidebar DownloadSideBar, linking to the doc download" + } +} diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/development/intro.md b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/development/intro.md new file mode 100644 index 0000000000..745da865b3 --- /dev/null +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/development/intro.md @@ -0,0 +1,38 @@ +--- +title: 开发者指南 +language: en +sidebar_label: Guide of Developer +pagination_label: Guide of Developer +toc_min_heading_level: 2 +toc_max_heading_level: 6 +pagination_prev: null +pagination_next: development/intro +keywords: + - developer +draft: false +last_update: + date: 10/21/2024 + author: Xiaoxiang Yu, Jiang Longfei +--- + +### 如何贡献 + +查看 [如何贡献](how_to_contribute.md) 文档。 + +### 源码仓库 + +Apache Kylin™ 源代码使用Git版本控制进行版本控制: + +| 仓库 | 连接 | +|:-------------------|:------------------------------------------------| +| Commits | https://github.com/apache/kylin/commits/kylin5 | +| Source Repo | https://github.com/apache/kylin/ | +| Mirrored to Gitbox | https://gitbox.apache.org/repos/asf?p=kylin.git | + +### 仓库 + +在[Apache JIRA](http://issues.apache.org/jira/browse/KYLIN)上跟踪 **Kylin项目** 的问题 + +### Wiki + +查看 [如何贡献 wiki](https://cwiki.apache.org/confluence/display/KYLIN/How+to+contribute+wiki). diff --git a/website/i18n/zh-Hans/docusaurus-theme-classic/footer.json b/website/i18n/zh-Hans/docusaurus-theme-classic/footer.json new file mode 100644 index 0000000000..577461cefd --- /dev/null +++ b/website/i18n/zh-Hans/docusaurus-theme-classic/footer.json @@ -0,0 +1,58 @@ +{ + "link.title.Docs": { + "message": "文档", + "description": "The title of the footer links column with title=Docs in the footer" + }, + "link.title.Community": { + "message": "社区", + "description": "The title of the footer links column with title=Community in the footer" + }, + "link.title.More": { + "message": "更多", + "description": "The title of the footer links column with title=More in the footer" + }, + "link.item.label.Quick Start": { + "message": "快速启动", + "description": "The label of footer link with label=Quick Start linking to /docs/quickstart/tutorial" + }, + "link.item.label.How to contribute": { + "message": "如何贡献", + "description": "The label of footer link with label=How to contribute linking to /docs/development/how_to_contribute" + }, + "link.item.label.Roadmap": { + "message": "路线图", + "description": "The label of footer link with label=Roadmap linking to blog/roadmap_of_kylin_50_cn" + }, + "link.item.label.JIRA": { + "message": "JIRA", + "description": "The label of footer link with label=JIRA linking to https://issues.apache.org/jira/projects/KYLIN/issues" + }, + "link.item.label.Mailing List Archives": { + "message": "邮件列表", + "description": "The label of footer link with label=Mailing List Archives linking to https://lists.apache.org/[email protected]" + }, + "link.item.label.Wiki": { + "message": "Wiki", + "description": "The label of footer link with label=Wiki linking to https://cwiki.apache.org/confluence/display/KYLIN/" + }, + "link.item.label.Stack Overflow": { + "message": "Stack Overflow", + "description": "The label of footer link with label=Stack Overflow linking to https://stackoverflow.com/questions/tagged/kylin" + }, + "link.item.label.Blogs": { + "message": "博客", + "description": "The label of footer link with label=Blogs linking to /blog" + }, + "link.item.label.Source Code": { + "message": "源码", + "description": "The label of footer link with label=Source Code linking to https://github.com/apache/kylin" + }, + "copyright": { + "message": "Copyright © 2024 Apache Software Foundation under the terms of the Apache License v2.\n <br>Apache Kylin and its logo are trademarks of the Apache Software Foundation.", + "description": "The footer copyright" + }, + "logo.alt": { + "message": "Apache", + "description": "The alt text of footer logo" + } +} diff --git a/website/i18n/zh-Hans/docusaurus-theme-classic/navbar.json b/website/i18n/zh-Hans/docusaurus-theme-classic/navbar.json new file mode 100644 index 0000000000..44eec99236 --- /dev/null +++ b/website/i18n/zh-Hans/docusaurus-theme-classic/navbar.json @@ -0,0 +1,30 @@ +{ + "title": { + "message": "Apache Kylin", + "description": "The title in the navbar" + }, + "logo.alt": { + "message": "Kylin Logo", + "description": "The alt text of navbar logo" + }, + "item.label.Documentation": { + "message": "文档", + "description": "Navbar item with label Documentation" + }, + "item.label.Community": { + "message": "社区", + "description": "Navbar item with label Community" + }, + "item.label.Development": { + "message": "开发者", + "description": "Navbar item with label Development" + }, + "item.label.Download": { + "message": "下载", + "description": "Navbar item with label Download" + }, + "item.label.Blogs": { + "message": "博客", + "description": "Navbar item with label Blogs" + } +}
