<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>绿色记忆 &#187; MongoDB</title>
	<atom:link href="https://blog.gmem.cc/tag/mongodb/feed" rel="self" type="application/rss+xml" />
	<link>https://blog.gmem.cc</link>
	<description></description>
	<lastBuildDate>Thu, 14 May 2026 10:21:02 +0000</lastBuildDate>
	<language>en-US</language>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.9.14</generator>
	<item>
		<title>Apache Drill学习笔记</title>
		<link>https://blog.gmem.cc/apache-drill-study-note</link>
		<comments>https://blog.gmem.cc/apache-drill-study-note#comments</comments>
		<pubDate>Tue, 08 Aug 2017 03:34:24 +0000</pubDate>
		<dc:creator><![CDATA[Alex]]></dc:creator>
				<category><![CDATA[BigData]]></category>
		<category><![CDATA[Database]]></category>
		<category><![CDATA[MongoDB]]></category>
		<category><![CDATA[学习笔记]]></category>

		<guid isPermaLink="false">https://blog.gmem.cc/?p=15283</guid>
		<description><![CDATA[<p>简介 Apache Drill是一个模式自由（Schema-free ）的、低延迟的、分布式的、可扩容的SQL查询引擎，可以让你使用熟悉的SQL语法对各种非关系型数据库进行操作。Drill支持针对PB级别数据的即席查询。Drill支持大量NoSQL数据和文件系统，包括MongoDB、HBase、HDFS。支持对不同数据源中的数据进行join操作。Drill支持Windows/Linux/Mac系统，可以很容易的在服务器集群中扩容。 Drill的优势包括： 支持模式自由的JSON模型，Drill是第一个、目前也是唯一的不对Schema做任何要求的分布式SQL引擎。这种模式自由类似于MongoDB。Drill在查询执行过程中可以自动发现Schema 即席的查询复杂的、半结构化的数据。你不需要对数据进行任何转换，Drill对SQL进行了直观的扩展，方面处理内嵌数据，就好像内嵌数据是普通的SQL列一样 真实的SQL语言，Drill支持标准的SQL 2003语法。支持DATE, INTERVAL, TIMESTAMP, VARCHAR等数据类型，以及关联子查询、JOIN子句 方便的和既有BI工具集成 针对Hive表的交互式查询 同时访问多个数据源 用户自定义函数支持，直接支持Hive用户定义函数 高性能、可扩容 基本概念 Drillbit Drill的核心是Drillbit服务，它负责接受客户请求、处理查询、返回查询结果。Drillbit可以被安装到并运行在数据库集群的所有节点上，这样在执行查询时可以减少网络流量。Drill通过ZooKeeper来维护集群成员状态、检查健康状况。 当你以SQL的形式发起一个查询时，查询被发送给Drill集群中的一个Drillbit，这个Drillbit成为领头（Foreman），它负责协作其它Drillbit以完成查询执行： 解析SQL语句，将SQL操作符转换为Drill理解的逻辑操作符。这些逻辑操作符共同组成了逻辑执行计划，描述了生成查询结果所需的操作、哪些数据源需要参与其中 Foreman把逻辑计划发送给基于成本的优化器，优化操作符的顺序，最终转换为物理执行计划 <a class="read-more" href="https://blog.gmem.cc/apache-drill-study-note">[...]</a></p>
<p>The post <a rel="nofollow" href="https://blog.gmem.cc/apache-drill-study-note">Apache Drill学习笔记</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></description>
				<content:encoded><![CDATA[<div class="wri_content_clear_both"><div class="blog_h1"><span class="graybg">简介</span></div>
<p>Apache Drill是一个模式自由（Schema-free ）的、低延迟的、分布式的、可扩容的SQL查询引擎，可以让你使用熟悉的SQL语法对各种非关系型数据库进行操作。Drill支持针对PB级别数据的即席查询。Drill支持大量NoSQL数据和文件系统，包括MongoDB、HBase、HDFS。支持对不同数据源中的数据进行join操作。Drill支持Windows/Linux/Mac系统，可以很容易的在服务器集群中扩容。</p>
<p>Drill的优势包括：</p>
<ol>
<li>支持模式自由的JSON模型，Drill是第一个、目前也是唯一的不对Schema做任何要求的分布式SQL引擎。这种模式自由类似于MongoDB。Drill在查询执行过程中可以自动发现Schema</li>
<li>即席的查询复杂的、半结构化的数据。你不需要对数据进行任何转换，Drill对SQL进行了直观的扩展，方面处理内嵌数据，就好像内嵌数据是普通的SQL列一样</li>
<li>真实的SQL语言，Drill支持标准的SQL 2003语法。支持DATE, INTERVAL, TIMESTAMP, VARCHAR等数据类型，以及关联子查询、JOIN子句</li>
<li>方便的和既有BI工具集成</li>
<li>针对Hive表的交互式查询</li>
<li>同时访问多个数据源</li>
<li>用户自定义函数支持，直接支持Hive用户定义函数</li>
<li>高性能、可扩容</li>
</ol>
<div class="blog_h2"><span class="graybg">基本概念</span></div>
<div class="blog_h3"><span class="graybg">Drillbit</span></div>
<p>Drill的核心是Drillbit服务，它负责接受客户请求、处理查询、返回查询结果。Drillbit可以被安装到并运行在数据库集群的所有节点上，这样在执行查询时可以减少网络流量。Drill通过ZooKeeper来维护集群成员状态、检查健康状况。</p>
<p>当你以SQL的形式发起一个查询时，查询被发送给Drill集群中的一个Drillbit，这个Drillbit成为领头（Foreman），它负责协作其它Drillbit以完成查询执行：</p>
<ol>
<li>解析SQL语句，将SQL操作符转换为Drill理解的逻辑操作符。这些逻辑操作符共同组成了逻辑执行计划，描述了生成查询结果所需的操作、哪些数据源需要参与其中</li>
<li>Foreman把逻辑计划发送给基于成本的优化器，优化操作符的顺序，最终转换为物理执行计划</li>
<li>Foreman中的并行器（parallelizer）把物理计划分为多个阶段 —— major/minor fragments。这些片断会并行的在所配置的数据源中执行</li>
</ol>
<div class="blog_h3"><span class="graybg">Major Fragments</span></div>
<p>构成执行计划的一个阶段，每个阶段可以由1-N个主片断构成，这些片断代表完成此阶段Drill必须执行的操作。Drill为每个主片段分配一个ID。</p>
<p>例如，为了针对两个文件进行哈希聚合，Drill可能创建具有两个阶段的查询计划，每个计划包含一个主片断。第一个阶段专注于扫描文件，第二个阶段则专注于数据的聚合。</p>
<p>Drill使用exchange operator来分隔多个主片段，所谓exchange可以是：</p>
<ol>
<li>数据位置的变化，或/和</li>
<li>物理计划的并行化</li>
</ol>
<p>一个exchange由sender/receiver组成，允许数据在节点之间流动。</p>
<p>主片断本身不负责任何查询任务的实际执行。每个主片段包含若干个从片断，从片断负责执行并完成查询。</p>
<p>你可以获得物理计划的JSON表示，修改之，然后通过Drill的SUBMIT PLAN命令提交执行。</p>
<div class="blog_h3"><span class="graybg">Minor Fragments</span></div>
<p>每个主片断被并行化为多个从片断。从片断是运行在一个线程内的逻辑工作单元（也叫slice）。每个从片断被分配一个ID。</p>
<p>Foreman中的并行器在执行期间把一个主片段拆分为1-N个从片断。Drill会根据数据局部性（data locality）把从片断调度到特定的节点上，并尽快的执行从片断（根据上流数据需求）。</p>
<p>从片断包含1-N个关系操作符，关系操作符执行关系型操作，例如scan, filter, join,group by。</p>
<p>从片断们可以形成树形结构，并分为root、intermediate、leaf三种角色。这种执行树仅仅包含一个运行在Foreman上的root从片断，需要执行的操作逐级下发，直到leaf从节点。leaf从节点与存储层交互或者访问磁盘数据，得到部分的结果，由上级节点进行聚合操作。 </p>
<div class="blog_h2"><span class="graybg">核心模块</span></div>
<p>每个Drillbit都由以下模块组成：</p>
<p><img class="aligncenter size-full wp-image-15291" src="https://cdn.gmem.cc/wp-content/uploads/2017/08/DrillbitModules.png" alt="drillbitmodules" width="545" height="232" /></p>
<div class="blog_h3"><span class="graybg">RPC endpoint</span></div>
<p>Drill暴露的低资源消耗的RPC协议，用于客户端连接。客户端可以直接连接到Drillbit，或者通过ZooKeeper连接。推荐使用后一种方式，以隔离Drill集群变化造成的影响。</p>
<div class="blog_h3"><span class="graybg">SQL parser</span></div>
<p>基于开源SQL解析器Calcite实现，用于解析客户端请求。解析结果是语言无关、计算机友好的逻辑计划。</p>
<div class="blog_h3"><span class="graybg">Storage plugin interface</span></div>
<p>屏蔽特定数据存储的差异性。存储插件的功能包括：</p>
<ol>
<li>从数据源获取元数据</li>
<li>读写数据</li>
<li>数据位置感知、一系列优化规则</li>
</ol>
<div class="blog_h2"><span class="graybg">客户端</span></div>
<p>访问Drill的途径包括：</p>
<ol>
<li>Drill Shell</li>
<li>Drill Web Console</li>
<li>ODBC/JDBC</li>
<li>C++ API</li>
</ol>
<div class="blog_h1"><span class="graybg">安装</span></div>
<div class="blog_h2"><span class="graybg">嵌入式安装</span></div>
<p>如果仅仅在单个节点上使用Drill，可以使用嵌入式安装。这种模式下，不需要安装ZooKeeper，也不需要进行配置。当你启动Drill shell时，本地的Drillbit服务自动启动。</p>
<p>安装步骤：</p>
<pre class="crayon-plain-tag">wget http://apache.mirrors.hoobly.com/drill/drill-1.11.0/apache-drill-1.11.0.tar.gz
tar xzf apache-drill.tar.gz</pre>
<p>要运行Drill，执行下面的命令以打开Drill Shell：</p>
<pre class="crayon-plain-tag">drill-embedded
0: jdbc:drill:zk=local&gt; 
# 命令提示符说明：
# 0 表示连接到drill的连接数
# jdbc为连接类型
# zk=local 作为ZooKeeper的代替</pre>
<p>或者执行<pre class="crayon-plain-tag">sqlline -u "jdbc:drill:zk=local"</pre></p>
<p>要退出Drill Shell，在Shell中输入<pre class="crayon-plain-tag">!quit</pre></p>
<p>要访问Web Console，在浏览器地址栏输入<pre class="crayon-plain-tag">http://127.0.0.1:8047/</pre></p>
<div class="blog_h2"><span class="graybg">分布式安装</span></div>
<p>要在Hadoop集群环境下使用Drill，可以使用分布式安装。ZooKeeper的分布式集群是必须的前提，你也需要对Drill进行配置，才能连接到各种数据源。</p>
<p>下载、解压后，修改配置文件：</p>
<pre class="crayon-plain-tag">drill.exec: {
  # Drill集群标识符
  cluster-id: "drillbits",
  # ZooKeeper连接字符串
  zk.connect: "172.21.0.1:2181,172.21.0.2:2181,172.21.0.3:2181"
}</pre>
<p>要以集群模式启动Drill，首先需要在集群的每个节点上启动守护程序Drillbit：</p>
<pre class="crayon-plain-tag"># 命令格式：drillbit.sh [--config &lt;conf-dir&gt;] (start|stop|status|restart|autorestart)
/home/alex/JavaEE/middleware/drill/bin/drillbit.sh --config /home/alex/JavaEE/middleware/drill/conf start</pre>
<p>要连接到分布式部署的Drill Shell，可以：</p>
<ol>
<li>执行drill-conf，此脚本使用conf/drill-override.conf配置</li>
<li>执行drill-localhost连接到运行在本机的ZooKeeper </li>
</ol>
<p>连接上以后，可以执行<pre class="crayon-plain-tag">SELECT * FROM sys.drillbits;</pre>查询Drill集群成员信息。</p>
<div class="blog_h2"><span class="graybg">在Docker中运行</span></div>
<p>参考如下Dockerfile：</p>
<pre class="crayon-plain-tag">FROM openjdk:8-jre

ENV CLUSTER_ID drillbits
ENV ZK_CONNECT 172.21.0.1:2181


RUN apt-get install -y wget tar

ADD docker-entrypoint.sh .
ADD apache-drill.tar.gz  .

RUN chmod +x docker-entrypoint.sh &amp;&amp; mv apache-drill-1.11.0 /opt/drill

ENTRYPOINT ["/docker-entrypoint.sh"]</pre>
<p>入口脚本：</p>
<pre class="crayon-plain-tag">#!/usr/bin/env bash

cat &lt;&lt; EOF  &gt; /opt/drill/conf/drill-override.conf
drill.exec: {
  cluster-id: "$CLUSTER_ID",
  zk.connect: "$ZK_CONNECT"
}
EOF

/opt/drill/bin/drillbit.sh --config /opt/drill/conf run</pre>
<p>创建并运行容器： </p>
<pre class="crayon-plain-tag">docker run -e ZK_CONNECT=172.21.0.1:2181,172.21.0.2:2181,172.21.0.3:2181 --name drill-14 \
           --network local --ip 172.21.1.14 -d docker.gmem.cc/drill </pre>
<div class="blog_h1"><span class="graybg">配置</span></div>
<div class="blog_h2"><span class="graybg">内存配置</span></div>
<p>你可以配置分配给Drillbit的用于处理查询的直接内存的量。默认配置是8G，在高负载下可能需要16G或者更多。</p>
<p>Drill使用Java的直接内存来存储执行中的操作，除非必须，它不会使用磁盘。这和MapReduce不同，后者将任务每个阶段的输出都存放在磁盘上。JVM的堆内存不限制Drillbit能够使用的直接内存。Drillbit的堆内存通常设置到4-8G就足够了，因为Drill避免在堆中写数据。</p>
<p>从1.5版本开始，Drill使用新的直接内存分配器，可以更好的使用、跟踪直接内存。由于这一变化，sort操作符可能因为内存不足而失败。</p>
<p>系统选项 planner.memory.max_query_memory_per_node 设置单个Drillbit中每个查询的sort操作符能够使用的内存量。如果一个查询计划中包含多个sort操作符，它们共享这一内存。如果sort查询出现内存问题，考虑增加此选项的值。如果问题仍然存在，考虑减小系统选项planner.width.max_per_node的值，该值控制单个节点的并行度。</p>
<div class="blog_h3"><span class="graybg">修改内存限制</span></div>
<p>在drill-env.sh中设置环境变量：</p>
<pre class="crayon-plain-tag"># 如果堆内存没有设置，将其设置为4G
export DRILL_HEAP=${DRILL_HEAP:-"4G”}  
# 如果直接内存没有设置，将其设置为8G
export DRILL_MAX_DIRECT_MEMORY=${DRILL_MAX_DIRECT_MEMORY:-"8G"}</pre>
<div class="blog_h2"><span class="graybg">安全配置</span></div>
<div class="blog_h3"><span class="graybg">角色</span></div>
<p>Drill提供两种角色：</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">角色</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>USER</td>
<td>可以对具有访问权限的数据执行查询。每个存储插件负责读写权限的管理</td>
</tr>
<tr>
<td>ADMIN</td>
<td>
<p>当启用身份验证时，仅仅具有Drill集群管理员角色的用户能够执行以下任务：</p>
<ol>
<li>使用ALTER SYSTEM来改变系统级选项</li>
<li>通过Web Console或者REST API来更新存储插件配置</li>
<li>提供和普通用户不同的导航栏</li>
<li>查看集群中正在运行的所有查询的profiles</li>
<li>取消运行中的查询</li>
</ol>
</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">身份模拟</span></div>
<p>用户身份模拟（Impersonation）允许一个服务代表客户端执行某个操作。默认的，身份模拟被关闭。</p>
<div class="blog_h3"><span class="graybg">PAM认证</span></div>
<p>Drill支持基于Linux PAM的身份验证。PAM允许和系统密码文件（/etc/passwd）或者LDAP等PAM实体进行交互以完成身份验证。</p>
<p>使用PAM验证时，运行Drill查询的用户必须存在于每一个Drill节点上。</p>
<div class="blog_h3"><span class="graybg">Kerberos认证</span></div>
<p>Drill支持Kerberos v5网络认证、客户端 - Drill的通信加密。需要配合JDBC驱动来使用该认证方式。</p>
<p>在启动时，一个Drillbit必须被验证。在运行时Drill使用和KDC共享的keytab文件，Drill使用该文件来验证票据的合法性。</p>
<p>配置<pre class="crayon-plain-tag">security.user.encryption.sasl.enabled</pre>参数为true，可以启用Kerberos加密 —— 保证客户端到Drillbit的数据安全。</p>
<p>你需要为Drill创建principal，可以：</p>
<pre class="crayon-plain-tag">kadmin
# 一个集群使用单个实体
# addprinc  &lt;username&gt;/&lt;clustername&gt;@&lt;REALM&gt;.COM 
addprinc  drill/drillbits@GMEM.CC</pre>
<p>你需要为上面的principal创建一个keytab文件：</p>
<pre class="crayon-plain-tag">ktadd -k /home/alex/JavaEE/middleware/drill/conf/drill.keytab drill/drillbits@GMEM.CC</pre>
<p>然后，为Drill配置文件添加：</p>
<pre class="crayon-plain-tag">drill.exec: {
  security: {
  	user.auth.enabled: true,
  	user.encryption.sasl.enabled: true,
  	auth.mechanisms: ["KERBEROS"],
  	auth.principal: "drill/drillbits@GMEM.CC",
  	auth.keytab: "/home/alex/JavaEE/middleware/drill/conf/drill.keytab"
  }
}</pre>
<p>并重启。</p>
<div class="blog_h2"><span class="graybg">配置选项</span></div>
<div class="blog_h3"><span class="graybg">关键启动选项</span></div>
<p>你可以在conf/drill-override.conf中配置启动选项，其中最常用的如下表：</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 30%; text-align: center;">选项</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>drill.exec.http.ssl_enabled</td>
<td>布尔（TRUE|FALSE），默认FALSE。是否启用HTTPS支持</td>
</tr>
<tr>
<td>drill.exec.sys.store.provider.class</td>
<td>设置持久化存储提供者（PStore），PStore保存配置数据、Profile</td>
</tr>
<tr>
<td>drill.exec.buffer.size</td>
<td>缓冲区大小，增加此配置可以加快查询速度</td>
</tr>
<tr>
<td>drill.exec.sort.external.spill.directories</td>
<td>进行Spool操作时使用的目录</td>
</tr>
<tr>
<td>drill.exec.zk.connect</td>
<td>提供ZooKeeper连接字符串</td>
</tr>
<tr>
<td>drill.exec.profiles.store.inmemory</td>
<td>布尔，默认FALSE。是否在内存中存放查询Profiles</td>
</tr>
<tr>
<td>drill.exec.profiles.store.capacity</td>
<td>上个选项取值TRUE时，内存中最多存放的查询Profiles数量</td>
</tr>
</tbody>
</table>
<div class="blog_h1"><span class="graybg">存储插件</span></div>
<p>Drill通过存储插件（Storage）连接到底层数据源。存储插件通常负责：</p>
<ol>
<li>连接到数据源，例如数据库、文件</li>
<li>优化Drill查询的执行</li>
<li>提供数据的位置信息</li>
<li>配置工作区、文件格式以读取数据</li>
</ol>
<p>常用的几个存储插件跟随Drill一起安装</p>
<div class="blog_h2"><span class="graybg">注册插件配置</span></div>
<p>所谓插件配置，就是连接到目标数据源的配置信息。Drill默认注册了这几个默认的插件配置：</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 15%; text-align: center;">插件配置</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>cp</td>
<td>指向Drill类路径中的JAR文件，你可以对其中的文件进行查询</td>
</tr>
<tr>
<td>dfs</td>
<td>指向本地文件系统。你可以使用对应的存储引擎配置指向任意分布式系统，例如Hadoop </td>
</tr>
<tr>
<td>hbase</td>
<td>提供到HBase的连接 </td>
</tr>
<tr>
<td>hive</td>
<td>将Drill和Hive的元数据抽象（文件、HBase）机制集成</td>
</tr>
<tr>
<td>mongo</td>
<td>提供到MongoDB的连接 </td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">注册MongoDB配置</span></div>
<p>通过Web Console连接（地址示例：http://172.21.1.14:8047/storage），可以注册插件配置。</p>
<p>点击Disable按钮可以禁用当前的配置，禁用后，show databases中对应的条目消失。点击Enable可以启用某个可用配置。输入存储插件名称，点击Create，可以建立新的插件配置。</p>
<p>插件配置都是JSON格式，MongoDB配置的示例：</p>
<pre class="crayon-plain-tag">{
  "type": "mongo",
  "connection": "mongodb://root:root@mongo-s1.gmem.cc:27017/",
  "enabled": true
}</pre>
<div class="blog_h3"><span class="graybg">测试配置正确性</span></div>
<p>打开drill-conf，输入命令验证连接是否正常：</p>
<pre class="crayon-plain-tag">0: jdbc:drill:&gt; show databases;
+---------------------+
|     SCHEMA_NAME     |
+---------------------+
| INFORMATION_SCHEMA  |
| mongo.admin         |
| mongo.bais          |
| mongo.config        |
| sys                 |
+---------------------+

# 上面的结果意味着已经连接到此配置，注意数据库名称的前缀，就是配置的名称

use mongo.bais;
select regNo,stocks[0].stockName as stock0Name from corps;
+----------------+-------------+
|     regNo      | stock0Name  |
+----------------+-------------+
| 3208261000000  | 汪震          |
+----------------+-------------+

# 上面的结果意味着查询测试成功 </pre>
<div class="blog_h1"><span class="graybg">JDBC/ODBC</span></div>
<p>除了Shell、Web Console以外，Drill还提供C++ API以及JDBC、ODBC驱动。</p>
<div class="blog_h2"><span class="graybg">JDBC</span></div>
<p>添加依赖以使用此驱动：</p>
<pre class="crayon-plain-tag">&lt;dependency&gt;
    &lt;groupId&gt;org.apache.drill.exec&lt;/groupId&gt;
    &lt;artifactId&gt;drill-jdbc&lt;/artifactId&gt;
    &lt;version&gt;1.11.0&lt;/version&gt;
&lt;/dependency&gt;</pre>
<div class="blog_h3"><span class="graybg">URL格式</span></div>
<pre class="crayon-plain-tag"># jdbc:drill:zk={ZooKeeper连接字符串}/drill/{Drill集群标识符};schema={存储插件配置.数据库名称}
jdbc:drill:zk=zookeeper-1.gmem.cc:2181,zookeeper-2.gmem.cc:2181,zookeeper-3.gmem.cc:2181/drill/drillbits;schema=mongo.bais</pre>
<div class="blog_h3"><span class="graybg">JDBC代码示例</span></div>
<pre class="crayon-plain-tag">Class.forName( "org.apache.drill.jdbc.Driver" );
String url = "jdbc:drill:zk=zookeeper-1.gmem.cc:2181/drill/drillbits;schema=mongo.bais";
Connection connection = DriverManager.getConnection( url );
Statement st = connection.createStatement();
ResultSet rs = st.executeQuery( "select regNo,stocks[0].stockName as stock0Name from corps" );
while ( rs.next() ) {
    System.out.println( rs.getString( 2 ) );
}</pre>
<div class="blog_h1"><span class="graybg">查询数据</span></div>
<div class="blog_h2"><span class="graybg">复杂数据结构</span></div>
<p>所谓复杂数据结构，是指与关系型数据库那种简单的表格形式（行、字段）不同的，具有复杂数据类型字段（内嵌结构）的数据结构。</p>
<p>Drill可以在执行查询请求的时候，发现数据的结构。类似于JSON、Parquet之类的嵌套数据结构不仅仅可以被简单的访问，Drill还提供特殊的操作符、函数对其进行钻取操作。这些操作符、函数能够：</p>
<ol>
<li>引用内嵌数据结构的值</li>
<li>访问数组元素、嵌套数组</li>
</ol>
<div class="blog_h2"><span class="graybg">JOIN操作</span></div>
<p>你可以使用SQL标准的join子句来连接两个表或/和文件。示例：</p>
<pre class="crayon-plain-tag">select c.regNo, c.corpName, o.name from corps as c join orgs as o on c.belongOrg = o._id where c.regCapi &gt; 10000;</pre>
<div class="blog_h2"><span class="graybg">访问嵌套数据</span></div>
<pre class="crayon-plain-tag">-- 访问内嵌文档
select c.address.detail as addr from corps as c;
-- 访问内嵌数组
select c.stocks[0].stockName from corps as c;
select c.stocks[0].stockName, c.stocks[0].subsCapi from corps as c;</pre>
<div class="blog_h1"><span class="graybg">日志与调试</span></div>
<p>Drill使用Logback作为默认的日志系统，日志配置位于conf/logback.xml。</p>
<p>默认的，日志被输出到文件系统，位于$DRILL_HOME/logs目录下，你可以在drill-env.sh中设置$DRILL_HOME环境变量。在每个Drill节点上，文件drillbit_queries.json记录每个查询的ID、profile信息。</p>
<div class="blog_h1"><span class="graybg">性能优化</span></div>
<div class="blog_h2"><span class="graybg">查询计划</span></div>
<p>要获得查询的执行计划，执行<pre class="crayon-plain-tag">explain plan for</pre>语句，示例：</p>
<pre class="crayon-plain-tag">explain plan for select regNo,corpName from bais.corps;</pre>
<p>从输出结果中，可以看到Drill如何访问底层数据源：</p>
<pre class="crayon-plain-tag"># explain plan for select regNo,corpName from bais.corps where regNo like '3208%';
00-00    Screen
00-01      Project(regNo=[$0], corpName=[$1])
00-02        UnionExchange
01-01          Scan(groupscan=[MongoGroupScan [MongoScanSpec=MongoScanSpec
                   [dbName=bais, collectionName=corps, filters=null], columns=[`regNo`, `corpName`]]])
                                                       # 没有过滤器，意味着需要全表扫描
# explain plan for select regNo,corpName from bais.corps where regNo &gt; '320800100' and regNo &lt; '320800200' limit 10;
00-00    Screen
00-01      Project(regNo=[$0], corpName=[$1])
00-02        SelectionVectorRemover
00-03          Limit(fetch=[10])
00-04            UnionExchange
01-01              SelectionVectorRemover
01-02                Limit(fetch=[10])
01-03                  Scan(groupscan=[MongoGroupScan [MongoScanSpec=MongoScanSpec 
                     [dbName=bais, collectionName=corps, 
                     filters=Document{{$and=[Document{{regNo=Document{{$gt=320800100}}}}, 
                     Document{{regNo=Document{{$lt=320800200}}}}]}}], columns=[`regNo`, `corpName`]]])
                                                       # 这里可以看到使用了MongoDB的查询过滤，可能利用到索引</pre>
<div class="blog_h1"><span class="graybg">SQL参考</span></div>
<p>Drill支持ANSI标准SQL，你可以使用统一的语法查询各种数据源。为了支持嵌套数据结构，Drill提供特殊的操作符和函数。</p>
<div class="blog_h2"><span class="graybg">数据类型</span></div>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">数据类型</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>BIGINT</td>
<td>8字节有符号整数</td>
</tr>
<tr>
<td>BINARY</td>
<td>变长二进制字符串，示例：B@e6d9eb7</td>
</tr>
<tr>
<td>BOOLEAN</td>
<td>布尔值，示例：true</td>
</tr>
<tr>
<td>DATE</td>
<td>YYYY-MM-DD格式的日期</td>
</tr>
<tr>
<td>DECIMAL(p,s)   DECIMAL(p,s)   NUMERIC(p,s)</td>
<td>38位精度数字</td>
</tr>
<tr>
<td>FLOAT</td>
<td>4字节浮点数</td>
</tr>
<tr>
<td>DOUBLE</td>
<td>8字节浮点数</td>
</tr>
<tr>
<td>INTEGER   INT</td>
<td>4字节有符号整数</td>
</tr>
<tr>
<td>INTERVAL</td>
<td>日/月时间间隔</td>
</tr>
<tr>
<td>SMALLINT</td>
<td>2字节有符号整数</td>
</tr>
<tr>
<td>TIME</td>
<td>HH:mm:ss格式的日期</td>
</tr>
<tr>
<td>TIMESTAMP</td>
<td>yyyy-MM-dd HH:mm:ss.SSS格式的时间戳</td>
</tr>
<tr>
<td>CHARACTER VARYING    CHARACTER    CHAR   VARCHAR</td>
<td>UTF-8字符串</td>
</tr>
<tr>
<td>Map</td>
<td>键值对形式的容器，KVGEN、FLATTEN函数用于处理此类型</td>
</tr>
<tr>
<td>Array</td>
<td>数组形式的容器，FLATTEN函数用于处理此类型</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">类型转换</span></div>
<p>使用CAST、CONVERT TO/FROM、TO_CHAR、TO_DATE、TO_NUMBER、TO_TIMESTAMP，可以进行显式的类型转换。某些类型之间可以进行隐式转换，NULL可以转换到任何类型。示例代码：</p>
<pre class="crayon-plain-tag">-- CAST (&lt;expression&gt; AS &lt;data type&gt;)
CAST( regNo as INT )

-- 把目标列转换为字节
CONVERT_TO (column, type)
-- 把regNo作为大端整数，转换为字节
CONVERT_TO(regNo , 'INT_BE')

-- 把字节转换为type
CONVERT_FROM(column, type)
-- 把字符串转换为JSON map
CONVERT_FROM('{x:100, y:215.6}' ,'JSON')

-- TO_CHAR (expression, 'format') 转换数字、日期、时间、时间戳为字符串形式
SELECT TO_CHAR(1256.789383, '#,###.###') FROM (VALUES(1));   -- 1,256.789
TO_CHAR((CAST('2008-2-23' AS DATE)), 'yyyy-MMM-dd')          -- 2008-Feb-23 
TO_CHAR(CAST('12:20:30' AS TIME), 'HH mm ss'                 --  12 20 30
TO_CHAR(CAST('2015-2-23 12:00:00' AS TIMESTAMP), 'yyyy MMM dd HH:mm:ss')
                                                             -- 2015 Feb 23 12:00:00 

-- TO_DATE (expression [, 'format']) 转换字符串或者UNIX时间戳为日期
TO_DATE('2015-FEB-23', 'yyyy-MM-dd')
-- TO_TIME (expression [, 'format']) 转换为时间
TO_TIME('12:20:30', 'HH:mm:ss')
TO_TIME(82855000)
-- TO_TIMESTAMP (expression [, 'format'])
TO_TIMESTAMP('2008-2-23 12:00:00', 'yyyy-MM-dd HH:mm:ss')</pre>
<div class="blog_h2"><span class="graybg">SQL函数</span></div>
<p>主要分为数学、日期、字符串、聚合等函数，参考<a href="https://drill.apache.org/docs/math-and-trig/">官方文档</a>。</p>
<div class="blog_h2"><span class="graybg">窗口函数</span></div>
<p>窗口函数针对一系列行进行计算操作，并为每一行返回单个值。这些值虽然归属到某个行，但是它可能<span style="background-color: #c0c0c0;">取决于其它多个行（这些行就是所谓窗口）</span>。</p>
<p>你可以使用<pre class="crayon-plain-tag">OVER()</pre>来定义一个窗口，此子句将窗口函数与其它的聚合类函数区分开来，一个查询可以使用多个窗口函数（对应一个或者多个窗口定义）。OVER()子句能够：</p>
<ol>
<li>定义对行进行分组（partition）的标准，聚合函数在这些分组上进行。这通过PARTITION BY子句实现</li>
<li>在一个分组内部，对行进行排序。这通过ORDER BY子句实现</li>
</ol>
<p>对于窗口函数，你需要注意：</p>
<ol>
<li>仅仅支持在查询的SELECT、ORDER BY字句中使用窗口函数</li>
<li>Drill在WHERE, GROUP BY, HAVING之后处理窗口函数</li>
<li>在聚合函数之后跟随OVER()导致其作为窗口函数使用</li>
<li>使用窗口函数，你可以针对窗口帧中任意数量的行进行聚合</li>
<li>如果要针对FLATTEN子句的生成的结果集执行窗口函数，应该在子查询中使用FLATTEN</li>
</ol>
<div class="blog_h3"><span class="graybg">语法</span></div>
<p>窗口函数完整的调用语法：</p>
<pre class="crayon-plain-tag">-- window_function指定一种窗口函数，这些函数可能和普通的聚合函数同名，识别它是否为窗口函数的唯一方法就是看看
-- 后面有没有OVER关键字。窗口函数在窗口内部进行聚合
-- expression 为列表达式
-- PARTITION BY关键字定义了窗口：
-- expr_lists 可以是  expression | column_name [, 其它expr_list ]
-- ORDER BY 定义窗口内排序规则，如果没有PARTITION BY则针对整个表格排序
--  order_lists 可以是 expression | column_name [ASC | DESC] [ NULLS { FIRST | LAST } ] [, 其它 order_list ]  
-- frame_clause 可以是：
-- { RANGE | ROWS } frame_start
-- { RANGE | ROWS } BETWEEN frame_start AND frame_end
-- frame_start 格式：UNBOUNDED PRECEDING 或者 CURRENT ROW 
-- frame_end 格式：CURRENT ROW 或者 UNBOUNDED FOLLOWING 
window_function (expression) OVER (
    [ PARTITION BY expr_list ]
    [ ORDER BY order_list ][ frame_clause ] )</pre>
<div class="blog_h3"><span class="graybg">分类</span></div>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 18%; text-align: center;">窗口函数分类</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>聚合</td>
<td>AVG() 计算平均值、COUNT()计算总数、MAX()计算最大值、MIN()计算最小值、SUM()求和</td>
</tr>
<tr>
<td>排名</td>
<td>返回当前行在分组中的排名：
<ol>
<li><span style="color: #333333; font-family: Ubuntu, 'Times New Roman', 'Bitstream Charter', Times, serif; font-size: 13px; line-height: 22px;">CUME_DIST() 返回相对排名：(高名次行数 + 同名次行数) / 总行数</span></li>
<li>DENSE_RANK() 根据窗口的ORDER BY表达式进行排序，排序号不存在gap，也就是说同名次（peer）不会导致后续名次跳号</li>
<li>NTILE() 尽可能的把窗口分组中的所有行划分到指定数量的排名组中</li>
<li>PERCENT_RANK()，百分比排名：(当前行数 - 1) / (分组总行数 - 1)</li>
<li>RANK()，类似于第2个，但是允许gap存在，也就是说两行并列的第1名之后的名次是3</li>
<li>ROW_NUMBER()，返回行号，取决于ORDER BY表达式</li>
</ol>
</td>
</tr>
<tr>
<td>值</td>
<td>
<ol>
<li><span style="color: #333333; font-family: Ubuntu, 'Times New Roman', 'Bitstream Charter', Times, serif; font-size: small;"><span style="line-height: 22px;">LAG()，返回分组中上一行的某个列（或者表达式）的值，如果没有上一行，返回NULL</span></span></li>
<li>LEAD()，返回分组中下一行的某个列（或者表达式）的值，如果没有下一行，返回NULL</li>
<li>FIRST_VALUE()，返回窗口中第一行的值</li>
<li>LAST_VALUE()，返回窗口中最后一行的值</li>
</ol>
</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">示例</span></div>
<pre class="crayon-plain-tag">-- 查询企业信息，为结果集的每一行增加列：当前企业类型的平均注册资金
select 
    cast(c.corpName as char) corpName, c.corpType, 
    avg( c.regCapi ) over( partition by c.corpType) as avgRegCapi  
from bais.corps c where c.regNo &gt;= '320100100' and c.regNo &lt; '320100200';</pre>
<div class="blog_h2"><span class="graybg">嵌套数据函数</span></div>
<p>嵌套数据函数用于访问内嵌式的数据结构，包括数组、映射、重复标量类型。不要在GROUP BY、ORDER BY子句或者在比较操作符中使用前述内嵌数据。Drill不支持 VARCHAR:REPEATED之间的比较。</p>
<div class="blog_h3"><span class="graybg">FLATTEN</span></div>
<p>把嵌套数据结构分解为单独的记录（行），示例：</p>
<pre class="crayon-plain-tag">-- 每个企业包含多个股东，股东为数组
SELECT FLATTEN(stocks) FROM bais.corps  WHERE stocks IS NOT NULL;</pre>
<div class="blog_h3"><span class="graybg">KVGEN</span></div>
<p>从一个映射中抽取键值对</p>
<div class="blog_h3"><span class="graybg">REPEATED_COUNT</span></div>
<p>返回数组的长度：<pre class="crayon-plain-tag">REPEATED_COUNT (array)</pre></p>
<div class="blog_h3"><span class="graybg">REPEATED_CONTAINS</span></div>
<p>在数组中搜索指定的关键字：<pre class="crayon-plain-tag">REPEATED_CONTAINS(array_name, keyword)</pre>，返回布尔值</p>
<div class="blog_h1"><span class="graybg">常见问题</span></div>
<div class="blog_h2"><span class="graybg">零散问题</span></div>
<div class="blog_h3"><span class="graybg">无法启动：Drillbit is disallowed to bind to loopback address in distributed mode.</span></div>
<p>原因：DNS将当前主机名解析到本地环回地址，可能需要更改/etc/hosts文件</p>
<div class="blog_h3"><span class="graybg">无法启动：Could not get canonical hostname.</span></div>
<p>原因：DNS没有正确配置</p>
<p>解决办法：如果网络中没有启用DNS服务，可以静态的修改/etc/hosts文件</p>
<div class="blog_h3"><span class="graybg">Failed to encode '***' in character set 'ISO-8859-1'</span></div>
<p>可以用这种方式来指定中文查询条件：</p>
<pre class="crayon-plain-tag">select * from bais.trades where name like _UTF16'%农产品%';</pre>
<div class="blog_h2"><span class="graybg">MongoDB问题</span></div>
<div class="blog_h3"><span class="graybg">身份验证失败：com.mongodb.MongoSecurityException: Exception authenticating MongoCredential</span></div>
<p>原因：如果分片集群启用了身份验证，不但需要建立集群上的用户，还要为每个复制集创建本地用户。如果报错信息中有servers=[{address=****而且地址是分片的（而不是mongos的）地址，说明就是分片的身份验证出错。</p>
</div><p>The post <a rel="nofollow" href="https://blog.gmem.cc/apache-drill-study-note">Apache Drill学习笔记</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.gmem.cc/apache-drill-study-note/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

<!-- Dynamic page generated in 0.097 seconds. -->
<!-- Cached page generated by WP-Super-Cache on 2026-05-15 01:28:05 -->

<!-- Compression = gzip -->