• 【用户画像】在ClickHouse中将宽表转换为bitmap表(源码实现)、用户分群架构设计、SpringBoot概述及使用


    一 在clickhouse中将宽表转换为Bitmap表

    1 任务目标

    为了能够更快的进行条件查询群体,把clickhouse中的宽表,通过行转列、聚合能操作转入Bitmap表。

    2 步骤分析

    • 读取标签定义表,用于获得标签名称和标签类型。
    • 建立四种不同数据类型的标签值表
    • 根据标签类型的不同要写入到四种不同的Bitmap表中。

    3 代码实现

    (1)搭建模块

    task-bitmap,添加scala目录,标记为源码,创建scala类com.hzy.userprofile.app.TaskBitmapApp。

    (2)pom文件

    添加如下配置信息

    <dependencies>
            <dependency>
                <groupId>com.hzy.userprofilegroupId>
                <artifactId>task-commonartifactId>
                <version>1.0-SNAPSHOTversion>
            dependency>
        dependencies>
    
        <build>
            <plugins>
                
                <plugin>
                    <groupId>net.alchim31.mavengroupId>
                    <artifactId>scala-maven-pluginartifactId>
                    <version>3.4.6version>
                    <executions>
                        <execution>
                            
                            <goals>
                                <goal>compilegoal>
                                <goal>testCompilegoal>
                            goals>
                        execution>
                    executions>
                plugin>
    
                <plugin>
                    <groupId>org.apache.maven.pluginsgroupId>
                    <artifactId>maven-assembly-pluginartifactId>
                    <version>3.0.0version>
                    <configuration>
                        <descriptorRefs>
                            <descriptorRef>jar-with-dependenciesdescriptorRef>
                        descriptorRefs>
                    configuration>
                    <executions>
                        <execution>
                            <id>make-assemblyid>
                            <phase>packagephase>
                            <goals>
                                <goal>singlegoal>
                            goals>
                        execution>
                    executions>
                plugin>
            plugins>
        build>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    (3)创建数据库

    在ClickHouse中userProfile1009库中创建以下四个表。

          create table user_tag_value_string
          ( tag_code String,
            tag_value String,
            us AggregateFunction(groupBitmap,UInt64),
            dt String )
          engine=AggregatingMergeTree
          partition by (dt,tag_code)
          order by tag_value;
            
          create table user_tag_value_decimal
          ( tag_code String,
            tag_value Decimal64(2),
            us AggregateFunction(groupBitmap,UInt64),
            dt String )
          engine=AggregatingMergeTree
          partition by (dt,tag_code)
          order by tag_value;
             
          create table user_tag_value_long
          ( tag_code String,
            tag_value UInt64,
            us AggregateFunction(groupBitmap,UInt64),
            dt String )
          engine=AggregatingMergeTree
          partition by (dt,tag_code)
          order by tag_value;
             
          create table user_tag_value_date
          ( tag_code String,
            tag_value Date,
            us AggregateFunction(groupBitmap,UInt64),
            dt String )
          engine=AggregatingMergeTree
          partition by (dt,tag_code)
          order by tag_value;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    (4)主程序

    (5)查看数据

    select tag_code,tag_value,bitmapToArray(us) from user_tag_value_string;
    
    • 1

    此时表中的数据存在重复现象,需要进行幂等性处理

    (6)打包发布

    把程序打包。

    在【流程任务管理】中增加任务,任务级别400。

    执行【手动调度任务】,选择数仓的业务日期。

    任务执行完成后查看结果。

    SELECT
        tag_code,
        tag_value,
        bitmapToArray(us)
    FROM user_tag_value_string
    
    • 1
    • 2
    • 3
    • 4
    • 5

    至此夜间处理的部分已经结束,即将数仓中的数据,一步一步的导入到ClickHouse中。

    接下来需要进行即席查询部分,即根据用户的需求立刻产生结果,用户根据页面操作,所见即所得,主要涉及web开发的技术。

    在这里插入图片描述

    数据存储到ClickHouse还不能被外部的应用使用,还需要进行一些人工干预,其中两个重点是

    • 用户标签明细及分析(洞察):根据一些标签进行统计分析,如这个标签涉及多少人,各个标签的分布比例,有点类似于数仓最后的分析,如饼状图等,只不过数仓不只是以用户这一个维度去分析。
    • 用户分群:下游应用都不是直接使用这写标签,需要将标签再进行一步组合,如根据运行,决策,广告等各种需求,将不同的标签进行归类,归类之后,将涉及到这些标签的人群集中起来,打成一个包,称为“人群包”,即一个一个的用户id。

    二 用户分群

    1 功能需求

    点击分区【创建分群】

    填写分群信息,需要填写的内容

    字段描述
    分群名称用户分群的信息
    业务日期(调试)实际生产环境应该是当前日期的前一日
    筛选条件根据三级标签进行筛选,
    分群人数用于圈选的用户一边调整筛选条件一边了解人群数
    更新类型自动就是每日定时更新,手动更新就是通过界面手动触发。

    2 结构分析

    在这里插入图片描述

    分群服务提供主要几个功能:

    • 接收页面传来的分群信息
    • 把分群基本信息保存在MySQL作为管理数据。
    • 利用clickhouse计算出分群的人群结果(Bitmap)保存起来,用于后续对该人群进行统计分析。
    • 将人群uid写入redis,用于营销、广告应用的高频访问数据。

    3 SpringBoot概述

    (1)Web服务概况

    Web服务(http服务),接收网页或者App应用发起的http请求,然后通过程序进行处理、计算、查询、存储等,再将结果返回给页面或App应用。

    在这里插入图片描述

    基本架构如下图:

    在这里插入图片描述

    • 平台化管理:越来越多的企业认为使用脚本管理不方便,容易出错。无论是离线还是实时数仓,更多的是将脚本变为页面,越来越多脚本中的SQL会被放到页面中。
    • 数据治理:需要大量的元数据管理,血缘管理等,虽然有许多的开源软件,但每个软件都是处理一部分功能,是彼此独立的,从整体掌控来说,对企业十分不方便,企业倾向的是一个统一的管理平台,也称为数据中台,无论是血缘,质量,指标,标准化管理,将所有的数据都放到一个页面中,对web开发有一定的需求。
    • 数据接口:上图的模式为web应用接收页面的请求,返回给页面,除了这种模式,还存在其他的web应用去调用这个web应用的情况,希望这个web应用对外提供一个接口,达到解耦合的目的,第二个目的是不希望其他应用访问我的数据库,可以发布一个对外的接口,让外部应用去访问这个数据接口,达到读取数据的目的。

    (2)SpringBoot简介

    Spring Boot 是由 Pivotal 团队提供的全新框架,其设计目的是用来简化新 Spring 应用的初始搭建以及开发过程。
    该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。

    (3)Springboot和SSM的关系

    Springboot整合了SpringMVC,Spring等核心功能。也就是说本质上实现功能的还是原有的SpringMVC,Spring的包,但是Springboot单独包装了一层,这样用户就不必直接对SpringMVC,Spring等,在xml中配置。

    (4)没有xml,去哪配置

    Springboot实际上就是把以前需要用户手工配置的部分,全部作为默认项。除非用户需要额外更改,不然不用配置。这就是所谓的:“约定大于配置”

    如果需要特别配置的时候,去修改application.properties (application.yml)

    (5)Springboot 的优势

    • 不再需要那些千篇一律,繁琐的xml文件。
    • 内嵌Tomcat,不再需要外部的Tomcat。
    • 更方便的和各个第三方工具(mysql,redis,elasticsearch,dubbo,kafka等整合),而只要维护一个配置文件即可。

    4 测试模块搭建

    (1)新建工程

    在user_profile_manage_0224新建工程【Spring initializr】,点击【next】

    具体信息如下:

    在这里插入图片描述

    点击next选择依赖,最终会形成一个pom文件,暂时选择最基本的配置,如下图

    在这里插入图片描述

    标红窗口内最好选择不带SNAPSHOT的。

    创建完成后可以在DemoSpbtApplication中运行应用程序,运行之后,可在127.0.0.1:8080进行访问。

    运行结果如下图:

    在这里插入图片描述

    (2)应用开发的分层

    分层职责涉及的标签
    controller主要用于对接http请求,返回http响应。@RestController @RequestMapping @RequestParam @RequestBody
    service用于处理业务请求@Service @Autowired
    mapper用于处理数据库操作请求@Select @Insert

    三层之间的关系

    在这里插入图片描述

    5 各层涉及到的标签

    标签职责工作层级
    @RestController标识请求的入口类 类上controller层
    @RequestMapping标识请求入口方法controller层
    @GetMapping标识请求入口方法controller层
    @PostMapping标识请求入口方法controller层
    @RequestParam接收请求URL路径中的参数controller层
    @PathVariable接收请求URL路径中的参数controller层
    @RequestBody接收Post请求体中的数据controller层
    @Autowired自动将接口实例化controller层、service层
    @Service标识了业务实现类的组件service层
    @Select标识要执行的select语句mapper
    @Insert标识要执行的insert语句mapper

    (1)controller层

    @RestController 和 @RequestMapping 标识类和方法
    • @RestController:标注的类不是普通的类,是所有程序的入口,其中的每个方法都可能成为入口方法。

    • @RequestMapping:在浏览器地址输入映射内容就能进入到对应方法。

      Get 和 Post请求都能接收。

    新建com.hzy.spbt.demo.controller.CustomerController类。

    编写方法。

    package com.hzy.spbt.demo.controller;
    
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController //@Controller
    public class CustomerController {
    
        @RequestMapping("/hello")
        public String helloCustomer(String hello){
            return "hello world";
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    运行程序,可以在浏览器端输入http://127.0.0.1:8080/hello进行访问这个方法,将hello word 输出到页面上。

    多数情况下,不仅需要输入/hello,还要在后面加上对应的参数,有如下三种参数:

    • 路径:如 ?x=xxxx&y=xxx&c=xxx,格式为name=value键值对形式。
    • 主键:如 /customer/3。
    • 请求体中的参数:在浏览器地址中不显示出来,往往这些参数比较复杂,内容特别多,如提交订单,填写用户信息等,一般组成一个JSON串传递给后台。
    @RequestParam获取路径

    实现功能:将参数通过@RequestParam(“name”)中的name,传递给java中的name,以键值对形式呈现。

    @RestController //@Controller
    public class CustomerController {
    
        @RequestMapping("/hello")
        public String helloCustomer(@RequestParam("name") String name){
            return "hello world " + name;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在浏览器输入http://127.0.0.1:8080/hello?name=zhangsan可以进行查看。

    @PathVariable获取主键

    获取路径中的值。

    @RequestMapping("/customer/{id}") //@GetMapping("/customer/{id}")
    public String getCustomer(@PathVariable("id") int userId){
        return "user id " + userId;
    }
    
    • 1
    • 2
    • 3
    • 4

    在浏览器输入http://127.0.0.1:8080/customer/32进行查看。

    @RequestBody获取请求体中的参数

    创建bean包,定义Customer Bean:com.hzy.spbt.demo.bean.Customer。

    @Data注解自动添加get,set方法。

    customer bean。

    package com.hzy.spbt.demo.bean;
    
    import lombok.Data;
    
    @Data
    public class Customer {
        String id;
        String name;
        int age;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
        @PostMapping("/customer")
        public String saveCustomer(@RequestBody Customer customer){
            return "save user information: " + customer.toString();
        }
    
    • 1
    • 2
    • 3
    • 4

    可以通过postman或者在浏览器中的插件市场中搜索rest,安装插件,完成测试。

    在这里插入图片描述

    输入地址 http://127.0.0.1:8080/customer/,在body中输入{"id":"101","name":"zhangsan","age":22}

    结果如下图:

    在这里插入图片描述

    在idea中添加断点,可以看到数据已经传到后台。

    在这里插入图片描述

    需要注意,bean中对象的格式要和JSON体中的格式高度一致。

    根据bean写json,根据json写bean,两种思路都可以。

    (2)service层

    @Service

    新建接口 com.hzy.spbt.demo.service.CustomerService

    package com.hzy.spbt.demo.service;
    
    import com.hzy.spbt.demo.bean.Customer;
    
    public interface CustomerService {
    
        public void saveCustomer(Customer customer);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    新建实现类 com.hzy.spbt.demo.service.impl.CustomerServiceImpl

    @Service:将类注册成为组件,为后面的自动装配做准备。在服务启动时,首先会发现自动装配,然后去整个服务的类中去找@Service的组件,查看哪个@Service匹配这个自动装配。容器会将这个对象new出来,是单态的,即一个容器中只有一个,供所有的方法使用。

    这个类会常驻在内存中,成为容器的组件,当哪个Controller需要的时候,自动完成装配,也称依赖注入。可以本地装配(接口和实现类在一台机器),也可以远程装配(接口和实现类在不同机器)。

    package com.hzy.spbt.demo.service.impl;
    
    import com.hzy.spbt.demo.bean.Customer;
    import com.hzy.spbt.demo.service.CustomerService;
    import org.springframework.stereotype.Service;
    
    @Service
    public class CustomerServiceImpl implements CustomerService {
    
        @Override
        public void saveCustomer(Customer customer) {
            System.out.println("service : saveCustomer:" + customer);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    @Autowired

    如果在方法内实现CustomerService customerService = new CustomerServiceImpl;,这个实现类是一个服务,作为一个web应用的服务,触发的频率是非常高的,那么每次调用这个服务都会创建对象,耗费大量的内存空间。

    将以上语句放在外面,先创建好,之后在各个方法中使用,这样也会带来一个问题,即一种接口可能有多个实现,所以一般会让容器对各个接口进行自动装配(注入),使用@Autowired。

    package com.hzy.spbt.demo.controller;
    
    import com.hzy.spbt.demo.bean.Customer;
    import com.hzy.spbt.demo.service.CustomerService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.*;
    
    @RestController //@Controller
    public class CustomerController {
        @Autowired
        CustomerService customerService;
    
        @PostMapping("/customer")
        public String saveCustomer(@RequestBody Customer customer){
            customerService.saveCustomer(customer);
            return "save user information: " + customer.toString();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
  • 相关阅读:
    音视频 ffmpeg命令提取PCM数据
    混淆矩阵——AI产品经理给我使劲看
    插入排序——直接插入排序
    [RK3588-Android12] 双HDMI+喇叭Speak同音问题
    MySQL LIKE BINARY 和 LIKE 模糊查询
    pytorch的pixel_shuffle转tflite文件
    初步认识OpenGL之QT
    华为数通HCIA-地址分类及子网划分
    Linux redis 安装
    项目管理中最常见的问题有哪些?
  • 原文地址:https://blog.csdn.net/weixin_43923463/article/details/127628552