使用hyperf结合DorisDB数仓的项目实战体验

项目背景#

做的是一个医疗数据分析项目,在此之前项目其实已经开发完成并且运行一段时间了,但是因为项目的体验极差以及对开发人员很不友好,与领导商议对项目进行重构

项目逻辑#

这个项目完全是一个分析性项目,大致的逻辑就是从病历库中计算数据到当前项目库中,然后从当前库查询展示到前台,主要涉及到数据排名、查询。
数据量:百万级

技术选型#

框架:本来打算用的 springboot 进行重构,因为公司整体的项目都在向 java 语言转移。但是因为当时业务部门对时间卡的很紧,考虑到这个项目跟语言的关系也不是很大,决定使用 hyperf 进行快速开发。
(因为以前的版本是用的 laravel 框架,可以很效率的转移到 hyperf)

数据库:之前用的是 mysql,但是计算和查询的速度都是不尽人意。因为不是直接的业务数据库,于是决定选用 OLAP 型数据库搭建数仓。最后在 clickhouse 和 Dorisdb 中选择了 Dorisdb

数仓搭建#

当前项目的计算数据全部基于病历数据库,所以我们要对病历数据库建一个数仓,然后在这个基础上新建几张计算结果的表就可以了,我们平台业务的要求是月更一次,所以对数据实时性的要求很低,但是后面上线之后我们改成了 T+1 更新模式。

数据同步#

因为对实时性没要求,所以采用的是 kettle 定时脚本的模式,这里不对 kettle 做过多介绍,其他类似产品还有 datax、canal

数仓使用#

数仓的具体使用每个产品也都有自己的特点,不做过多阐述。这里具体说下 Dorisdb,他本身的语法和 mysql 极度相似,所以好多 sql 都是通用的,像 laravel、hyperf 这些框架的 model 也是直接支持使用的。这也是当时选用这个数据库的原因之一。其他单独特性自行去官网了解

计算数据#

到这一步才算把 swoole 的优势发挥出来,dorisdb 本身也比 mysql 快,但是框架也起到了很大的作用
直接贴结果,前后计算时间对比:

60w 病历数据 (汇总字段 500 个左右) + 100w + 随访数据时间参考:

老版 (laravel + mysql) 50 小时左右

新版 (hyperf + dorisdb) 开启多协程 (50 个) 40 分钟左右

新版 (hyperf + dorisdb) 一个协程 5 个小时

访问速度#

在老版中最慢的一个接口需要 2min,在更新后接口都能达到秒级

ab 测试结果因为老项目停运了当时结果也没保存,所以就不贴出来了

访问速度整体看来,我认为作用是数仓 > 框架处理的,因为之前接口慢的主要原因其实就是在于数据库层面

注意事项#

hyperf 框架日常使用和 laravel 基本没有太大区别,只是在运行的时候是基于一个进程的。所以直接改动代码可能不会像 fpm 框架那样直接看到效果,推荐使用官方推荐的热部署

轮子方面确实与 laravel 还有很大差距,目前基于 hyperf 的拓展还是很少的,但是有很多拓展在两种框架都是通用的

中同一个客户端的请求都是单例的,所以你不能直接更改 request 数据,这个问题我当时郁闷了好久

如果有个跑数据脚本,想启用多个协程,一定要控制协程的数量,比如我跑数据开启的是 50 个协程,那么一旦数量达到 50 个后,就要等全部协程工作完成后再开启下一轮的 50 个协程。这个具体的协程数量可以根据数据库配置和服务器配置自己定义

协程管理可以用 WaitGroup 来控制,用法也很简单,swoole 官方文档和 hyperf 文档上都有介绍

贴一段跑数据的 demo

$wg = new WaitGroup();
RedisUtil::set($this->redisKey, 0);
foreach ($provinces as $province) {
    for ($year = 2017; $year <= date('Y'); $year++) {
        $cities = $provincesData[$province];
        foreach ($cities as $city) {
            // 如果协程数量达到50  挂起等待执行完成后开启新协程
            if ($wg->count() >= 50) {
                $wg->wait();
            }
            $wg->add();
            // 具体业务代码

            RedisUtil::incrBy($this->redisKey, count($all));
            SyncLog::updateLog($log_id, RedisUtil::get($this->redisKey), 0);
            $this->output->writeln("同步数量:".RedisUtil::get($this->redisKey));
            $wg->done();
        }

    }
}

部署#

传统的 php 项目配合 nginx 转发一下目录可能就 ok 了,但是 swoole 的 http server 是基于一个进程的,所以可以用 nginx 做一个反向代理。

因为有进程的原因,还是使用 docker 部署,不然还要守护这个进程,而且官方的 dockerfile 也为我们准备了最合适的 php 版本,不用额外去装一些拓展。

关于项目重启期间访问失败的问题,如果一定要项目不能停止,可以将项目分两个端口部署两次用 nginx 做负载均衡即可

这里贴出 dockerfile 和部署脚本

# Default Dockerfile
#
# @link     https://www.hyperf.io
# @document https://hyperf.wiki
# @contact  group@hyperf.io
# @license  https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE

FROM hyperf/hyperf:7.4-alpine-v3.11-swoole
LABEL maintainer="Hyperf Developers <group@hyperf.io>" version="1.0" license="MIT" app.name="Hyperf"

##
# ---------- env settings ----------
##
# --build-arg timezone=Asia/Shanghai
ARG timezone

ENV TIMEZONE=${timezone:-"Asia/Shanghai"} \
    #APP_ENV=prod \
    SCAN_CACHEABLE=(true)

# update
RUN set -ex \
    # show php version and extensions
    && php -v \
    && php -m \
    && php --ri swoole \
    #  ---------- some config ----------
    && cd /etc/php7 \
    # - config PHP
    && { \
        echo "upload_max_filesize=128M"; \
        echo "post_max_size=128M"; \
        echo "memory_limit=-1"; \
        echo "date.timezone=${TIMEZONE}"; \
    } | tee conf.d/99_overrides.ini \
    # - config timezone
    && ln -sf /usr/share/zoneinfo/${TIMEZONE} /etc/localtime \
    && echo "${TIMEZONE}" > /etc/timezone \
    # ---------- clear works ----------
    && rm -rf /var/cache/apk/* /tmp/* /usr/share/man \
    && echo -e "\033[42;37m Build Completed :).\033[0m\n"

WORKDIR /opt/www

# Composer Cache
# COPY ./composer.* /opt/www/
# RUN composer install --no-dev --no-scripts

COPY . /opt/www
RUN  composer install --no-dev -o && php bin/hyperf.php

EXPOSE 9501

ENTRYPOINT ["php", "/opt/www/bin/hyperf.php", "start"]
# 容器名
containerName='你的项目名'
cid=$(docker ps | grep $containerName | awk '{print $1}')
if [ -n "$cid" ]
then
    echo "prepare to stop $cid"
    docker stop $cid
    echo "stop $cid success"
    echo "prepare to remove $cid"
    docker rm $cid
    echo "stop $cid success"
else
    echo "no such container"
fi
# 镜像名
oldBuild="qc_doris"
oldImageID=$(docker images | grep $oldBuild | awk '{print $3}')
if [ -n "$oldImageID" ]
then
    echo "prepare to remove old image"
    docker rmi $oldImageID
    echo "remove $oldImageID success"
else
    echo "no such image"
fi
docker build -t qc_doris:0.1 .
echo "build success"
#run
docker run -d -v $(pwd)/runtime/logs:/opt/www/runtime/logs \
  -v $(pwd)/.env:/opt/www/.env \
  -v $(pwd)/static:/opt/www/static \
  -p 9501:9501 --privileged=true  --name xxx xxx:0.1
echo "running success"
# -d:后台运行;  -p:将容器暴露端口映射到服务器端口     imageName

总结#

如果从 laravel 过渡到 hyperf 整体还是十分丝滑的,甚至你不怎么了解 swoole 也可以直接使用,但是还是推荐大家把 swoole 官方的文档啃一遍,有助于理解框架的一些设计。使用体验目前还不错

项目整体重构时间加上数仓建设其实只用了一个月,去年年底上线到现在已经稳定运行了有一段时间。这次重构让业务部门很满意,也让领导很满意。这里推荐大家去拥抱新的技术但是同时也不要为了技术而技术。一个项目开发不仅仅是 IT 部门的工作还要与业务有各种磨合,如果有些吃力不讨好的技术非要上我觉得是完全没有必要的。

公司因为整体项目都在用 java 重构,下次有时间我会讲讲如何将 php 项目用 java 进行重构以及遇到的各种问题和解决办法。

本作品采用《CC 协议》,转载必须注明作者和本文链接
本帖由系统于 2年前 自动加精
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 18
laravel_peng

file

摘自你的文章:访问速度整体看来,我认为作用是数仓 > 框架处理的,因为之前接口慢的主要原因其实就是在于数据库层面

看样子在处理大量数据汇总方面,hyperf + dorisdb 确实快很多。其实我有些疑问?

  1. 如果使用 hyperf 框架,但协程只设置为一个,和正常使用 Laravel 框架执行效果会不会一样?
  2. 如果上面的猜想能够成立的话,是否可以理解是 dorisdb 数据库让你在处理大量数据汇总上速度提升了 10 倍。
  3. 还有一点就是跑脚本 50 个小时这个,你们真的试过了么(感觉这个耗时好长啊)?还是说是预测的时间。
2年前 评论
卖蛋饼等你下课 (楼主) 2年前
卖蛋饼等你下课 (楼主) 2年前
李铭昕

厉害👍🏻

2年前 评论

为什么社区不添加一个 hyperf 版块

2年前 评论
黑将军 2年前

Doris 和 MySQL 的表结构是怎么同步的呢?手动创建吗

2年前 评论
卖蛋饼等你下课 (楼主) 2年前

hyperf 怎么配置 Dorisdb 的,还有模型那边是怎么对数据做查询等等的呀,楼主没分享下

2年前 评论
卖蛋饼等你下课 (楼主) 2年前

楼主大大你好,我这边用 model 直接写查询语句,总是提示不支持命令。即使是在 selelect 里面加上了查询的字段,没用用 * 也会这样。想问一下这个地方有没有什么特殊的配置需要变更或者新增

2年前 评论
卖蛋饼等你下课 (楼主) 2年前
卖蛋饼等你下课 (楼主) 2年前

我想问下,你们是整个库都迁移到 doris 上吗,还是单独的把一些需要的表格迁移过去了,还是只是在 doris 中进行数据操作,然后 mysql 这边也存储

4个月前 评论
卖蛋饼等你下课 (楼主) 4个月前

file楼主的 doris 是怎么连的,我的不行

4个月前 评论
中文 4个月前