知足常乐

知足常乐

Presto初体验

2021-09-20

Presto是一个facebook开源的分布式SQL查询引擎,适用于交互式分析查询,数据量支持GB到PB字节。Presto 可扩展到任何数据源,它的设计采用了存储抽象化,以便于轻松地构建可插入的连接器。因此,Presto 拥有大量连接器,既可用于非关系数据源,例如 Hadoop 分布式文件系统 (HDFS)、Amazon S3、Cassandra、MongoDB 和 HBase,又可用于关系源,例如 MySQL、PostgreSQL、Amazon Redshift、Microsoft SQL Server 和 Teradata。数据在其存储位置接受查询,无需将其移动到独立的分析系统中。

1.用Presto干什么

Presto能干的事情非常多,搞大数据的应该使用过Hive,它们都是基于SQL引擎来做一些数据的分析和计算,我们今天用Presto来进行多数据源查询sql,数据源使用mysql。

2.部署Presto

Presto采用典型的master-slave模型,我们为了部署方便,就使用单机单节点部署,让它即是主节点也是工作节点,写这篇文章的时候,Presto的最新版本是0.261,我们就使用这个版本来部署。

2.1 下载二进制包

在linux节点机上执行以下命令

wget https://repo1.maven.org/maven2/com/facebook/presto/presto-server/0.261/presto-server-0.261.tar.gz

下载好之后解压文件

tar -zxvf presto-server-0.261.tar.gz 

2.2 编辑配置文件

解压之后我们进入presto-server-0.261 目录下(/opt/app/presto-server-0.261)
创建一个etc目录用来放置配置文件
创建一个data目录放置presto产生的数据文件

mkdir etc
mkdir data

在etc(/opt/app/presto-server-0.261/etc)目录下我们要创建几个presto需要的配置文件

2.2.1 node.properties

vi node.properties

执行完上边命令 cp以下内容, node.data-dir属性按照自己的物理路径

node.environment=production
node.id=ffffffff-ffff-ffff-ffff-ffffffffffff
node.data-dir=/opt/app/presto-server-0.261/data

2.2.2 jvm.config

vi jvm.config

执行完上边命令 cp以下内容, 内存按需设置,16G是官方给的参考配置

-server
-Xmx16G
-XX:+UseG1GC
-XX:G1HeapRegionSize=32M
-XX:+UseGCOverheadLimit
-XX:+ExplicitGCInvokesConcurrent
-XX:+HeapDumpOnOutOfMemoryError
-XX:+ExitOnOutOfMemoryError

2.2.3 config.properties

vi config.properties

执行完上边命令 cp以下内容, 这里我们是按照单机单节点配置的,如果需要集群主从配置的话,参考文末的官方文档即可,端口号自己配置,不要与自身服务冲突即可

coordinator=true
node-scheduler.include-coordinator=true
http-server.http.port=20000
query.max-memory=5GB
query.max-memory-per-node=1GB
query.max-total-memory-per-node=2GB
discovery-server.enabled=true
discovery.uri=http://localhost:20000

2.2.4 log.properties

vi config.properties

执行完上边命令 cp以下内容,默认的最低级别是INFO 共有四个级别:DEBUG,INFO,WARN和ERROR

com.facebook.presto=INFO

2.3 启动Presto

回到我们安装presto的bin(/opt/app/presto-server-0.261/bin)目录下
我们可以先前台启动看下日志

./launcher run

我这里出现了一点问题,启动的时候报出了以下的错误

/usr/bin/env: ‘python’: No such file or directory

我们看一下启动脚本的内容,可以看到最后一行执行了同目录下的launcher.py脚本。。。。我先去装个py先

#!/bin/sh -eu
#
# Launcher for Airlift applications
...
...
...
#
# Run with --help to see options.
#

exec "$(dirname "$0")/launcher.py" "$@"

OK,我现在已经安装好了python,我们再次执行启动命令,又出问题了~

2021-09-20T00:55:33.044+0800	ERROR	main	com.facebook.presto.server.PrestoServer	Unable to create injector, see the following errors:

1) Error injecting constructor, java.io.IOException: java.io.IOException: Can not attach to current VM (try adding '-Djdk.attach.allowAttachSelf=true' to the JVM config)
...
...
...
2) Error injecting constructor, java.io.IOException: java.io.IOException: Can not attach to current VM (try adding '-Djdk.attach.allowAttachSelf=true' to the JVM config)
  at com.facebook.airlift.jmx.JmxAgent9.<init>(JmxAgent9.java:47)

看样子是jvm出了问题,让我尝试修改配置,果断去github上搜索一下
https://github.com/prestodb/presto/issues/13216
看了下我的jdk版本.嗯 是jdk11的. 但。。这是19年的bug啊, 算了,先修改下jvm的配置看看吧
进入到我们的etc(/opt/app/presto-server-0.261/etc)目录下

vi jvm.config

执行上边命令 修改jvm的配置

-server
...
...
...
-Djdk.attach.allowAttachSelf=true

再次执行启动命令 当你看到SERVER STARTED 的日志时,就代表presto服务端已经启动完毕了

2021-09-20T01:09:14.892+0800	INFO	main	com.facebook.presto.server.PluginManager	
...
...
...
com.facebook.presto.storage.TempStorageManager	-- Loaded temp storage local --
2021-09-20T01:09:15.082+0800	INFO	main	com.facebook.presto.server.PrestoServer	======== SERVER STARTED ========

访问你在config.properties中设置的端口号,即可看到presto服务提供的dashboar界面。
如果你无法访问,请看下后台是不是进程断掉了,要保证内存足够。

image.png

这个时候我们就可以后台启动presto了
回到我们安装presto的bin(/opt/app/presto-server-0.261/bin)目录下

./launcher start

3.简单使用

presto本身不存储数据,只是用来对数据进行查询计算,所以我们使用之前先确定好数据源,我们这里就启动两个mysql数据源来提供数据.

3.1 数据源配置

我们在etc(/opt/app/presto-server-0.261/etc)目录下创建catalog目录

mkdir catalog

catalog目录是存储连接器的目录,你可以理解为是存储数据源配置信息的目录
在catalog目录下创建两个mysql的配置(其它数据源可参考文末的链接)

vi mysql1.properties

执行完上边的命令后,cp下边的配置,对应的配置信息改为自己的

connector.name=mysql
connection-url=jdbc:mysql://192.168.73.128:3306
connection-user=root
connection-password=123456

我们再创建第二个配置文件

vi mysql2.properties

执行完上边的命令后,cp下边的配置,对应的配置信息改为自己的

connector.name=mysql
connection-url=jdbc:mysql://192.168.73.128:3307
connection-user=root
connection-password=123456

完事之后,我们回到bin(/opt/app/presto-server-0.261/bin)目录下
刚刚我们为了验证是否能启动成功已经把presto启动了,我们执行以下命令先停止再启动

./launcher stop
./launcher start

3.2 使用presto-cli连接服务端

presto提供多种连接服务端的方式,我们先用官方制作的cli客户端来与服务端进行交互

3.2.1 下载presto-cli.jar

wget https://repo1.maven.org/maven2/com/facebook/presto/presto-cli/0.261/presto-cli-0.261-executable.jar

3.2.2 启动连接

先给下载的jar包授权

chmod +x presto-cli-0.261-executable.jar

然后执行jar包的启动命令,--server参数 填写自己设置的服务端端口,我是在本机部署本机启动 所以就填localhost了。

./presto-cli-0.261-executable.jar --server localhost:20000

启动之后就会进入一个可交互的界面
输入 show catalogs 命令就会发现可以看到我们刚刚配置的两个mysql数据源和一个系统system数据源

presto> show catalogs;
 Catalog 
---------
 mysql1  
 mysql2  
 system  
(3 rows)

Query 20210920_084311_00002_hb89m, FINISHED, 1 node
Splits: 19 total, 19 done (100.00%)
298ms [0 rows, 0B] [0 rows/s, 0B/s]

3.2.3 进行sql查询

我们知道mysql的sys系统库下有一个sys_config表,我们来执行sql查看下里边的内容

presto> select * from mysql1.sys.sys_config;
               variable               | value |        set_time         | set_by 
--------------------------------------+-------+-------------------------+--------
 diagnostics.allow_i_s_tables         | OFF   | 2021-09-19 16:09:44.000 | NULL   
 diagnostics.include_raw              | OFF   | 2021-09-19 16:09:44.000 | NULL   
 ps_thread_trx_info.max_length        | 65535 | 2021-09-19 16:09:44.000 | NULL   
 statement_performance_analyzer.limit | 100   | 2021-09-19 16:09:44.000 | NULL   
 statement_performance_analyzer.view  | NULL  | 2021-09-19 16:09:44.000 | NULL   
 statement_truncate_len               | 64    | 2021-09-19 16:09:44.000 | NULL   
(6 rows)

Query 20210920_084658_00004_hb89m, FINISHED, 1 node
Splits: 17 total, 17 done (100.00%)
323ms [6 rows, 0B] [18 rows/s, 0B/s]

我们在Dashboard上其实也可以看到整个系统的状态,包括执行过的语句
image.png

3.3 使用presto-jdbc连接服务端

cli一般适合运维人员上去操作,搞开发的一般还是使用驱动器在代码中进行操作,这里我就主要用presto-jdbc演示连接server端并进行多mysql数据源的sql查询。

3.3.1 准备数据

在创建java项目之前,我们先给两个mysql数据源创建好数据
这里用很简单的例子来举例,并不对真实业务场景进行测试

第一个数据源创建user库,在user库中创建USER_INFO数据表,里边很简单,id和name;
image.png

第二个数据源创建cost库,在cost库中创建USER_COST数据表,很简单,只有userID和cost;
image.png

当我把上边的数据准备好,在presto-cli执行sql语句时,出问题了。。

3.3.2 presto对mysql的BUG??

当我执行以下sql语句的时候,可以很清晰的看到,user_info变成小写了

presto> show tables from mysql1.user;
   Table   
-----------
 user_info 
(1 row)

我本以为小写是presto处理了,但是当我执行以下语句

presto> select * from mysql1.user.USER_INFO;
Query 20210920_100117_00028_hb89m failed: line 1:15: Table mysql1.user.user_info does not exist

很明显,它去mysql中查询的时候按照小写来执行了,但我mysql数据库中是大写的表名
再次去github绕一圈
https://github.com/prestodb/presto/issues/14930
不知道是我操作有问题还是什么的,这好像也是很久之前的一个问题了,但是现在还是没有解决.

那现在只能把表名都改成小写了.还可以参考文末的链接把mysql的表名修改成不区分大小写。
再次查询

presto> select * from mysql1.user.USER_INFO;
 id |  name  
----+--------
  1 | 张三   
  2 | 李四   
  3 | 王五   
  4 | 哈哈   
  5 | 你好   
  6 | Jack   
  7 | Mary   
  8 | 方鹏博 
  9 | Root   
(9 rows)

3.3.3 jdbc进行操作

我创建了一个web的脚手架

首先在pom文件中引入presto的依赖
pom.xml

...
 <dependency>
            <groupId>com.facebook.presto</groupId>
            <artifactId>presto-jdbc</artifactId>
            <version>0.261</version>
 </dependency>
...

然后编写配置文件
application.properties

# 应用名称
spring.application.name=presto-java
server.port=20001
presto.jdbc.url=jdbc:presto://192.168.73.128:20000

创建DBUtil工具类

@Component
public class DBUtil {
    @Value("${presto.jdbc.url}")
    public String url;

    public Connection getConnection() throws Exception {
        Class.forName("com.facebook.presto.jdbc.PrestoDriver");
        Connection conn = DriverManager.getConnection(url,"root","");
        conn.setCatalog("mysql");
        return conn;
    }
}

编写Controller层

@RestController
public class UserController {
    @Autowired
    PrestoDao prestoDao;
    //查询未缴学费的学生姓名列表
    @GetMapping("notPayUser")
    public List<String> notPayUser (){
        try {
            return prestoDao.notPayUser();
        } catch (Exception e) {
            return null;
        }
    }
}

编写接口实现层

注意这里我们的sql语句是同时查询两个不同数据源的不同数据库
String sql = "select name from mysql1.user.user_info where id not in (select userId from mysql2.cost.user_cost)";

@Service
public class PrestoDao {
    @Autowired
    DBUtil dbUtil;
    public List<String> notPayUser() throws Exception {
        List<String> result = new ArrayList<>();
        String sql = "select name from mysql1.user.user_info where id not in (select userId from mysql2.cost.user_cost)";
        Connection connection = dbUtil.getConnection();
        Statement statement = connection.createStatement();
        ResultSet resultSet = statement.executeQuery(sql);
        while (resultSet.next()){
            result.add(resultSet.getString("name"));
        }
        return result;
    }
}

RUN起来
请求我们写的接口
image.png

可以看到是不是这几个同学的名单没有在cost表中

image.png

当然,真实的业务场景肯定不是这么简单,有的时候可能我们的sql要跑几十分钟甚至几个小时。
presto和类似的hive等对比还是有很大优势的,详细参考文末的链接

上边只是对presto的简单使用,多数据源联合查询只是其中的小小部,presto的功能远远不止这些。希望在后边能有机会去了解它。只是简单的部署就遇到了两个BUG,且都是挺久了,不知道是没修复还是怎么回事,是有点尴尬,那个mysql表名区分大小写的没修复是真的有问题啊。。。

4.引用

NameLink
Presto 0.261 文档https://prestodb.io/docs/current/index.html
Presto连接器(数据源)配置https://prestodb.io/docs/current/connector.html
MySQL修改lower_case_table_names参数https://www.cnblogs.com/jyzhao/p/13218753.html
如何比较Hive,Spark,Impala和Presto?https://zhuanlan.zhihu.com/p/161000135