【用户画像】功能实现值写入ClickHouse人群包、预估和更新分群人数,NoSQL数据库介绍

news2024/11/19 13:30:09

文章目录

  • 一 写入ClickHouse人群包
    • 1 组合查询Bitmap表SQL代码实现
      • (1)SQL语句分析
      • (2)实现思路
      • (3)实现过程
        • controller层
        • service层
        • Taginfo实现类
        • mapper层
    • 2 人群包代码实现
      • (1)配置文件
      • (2)UserGroupMapper
      • (3)UserGroupServiceImpl实现类
  • 二 预估分群人数
  • 三 更新分群人数
  • 四 NoSQL数据库
    • 1 NoSQL数据库简介
      • (1)概述
      • (2)NoSQL适用场景
      • (3)NoSQL不适用场景
    • 2 NoSQL家族
      • (1)Memcache(内存数据库)
      • (2)Redis(内存数据库)
      • (3)Mongodb(文档数据库)
      • (4)Hbase(列式数据库)
      • (5)Cassandra(列式数据库)
    • 3 DB-Engines数据库排名

一 写入ClickHouse人群包

1 组合查询Bitmap表SQL代码实现

第一步插入数据工作已经完成,现进行第二步,需要根据筛选条件进行计算,具体工作如下图:

在这里插入图片描述

(1)SQL语句分析

select
bitmapToArray(
bitmapAnd( 
  bitmapAnd(
    (select groupBitmapMergeState(us) from user_tag_value_string where tag_code='TG_BASE_PERSONA_GENDER' and tag_value='男' and dt='2022-10-16')
      ,
    (select groupBitmapMergeState(us) from user_tag_value_string where tag_code='TG_BASE_PERSONA_AGEGROUP' and tag_value in ('90后','80后','70后') and dt='2022-10-16')
  ),
  (select groupBitmapMergeState(us) from user_tag_value_long where tag_code='TG_TG_BEHAVIOR_ORDER_LAST30d_CT' and tag_value>=1 and dt='2022-10-16')
)
    )

在页面端,创建分群中想要选择条件,点击保存就能将大概人数估计出来,需要将以上SQL拼接出来,传递进去,然后执行。

其中子查询中的变量为

  • 表名
    • 需要知道标签的值类型:去找标签编码,然后去查询标签的定义,最后得到标签的值类型 TagCondition.tag_code。
  • 标签编码 TagCondition.tag_code。
  • 标签值
    • 值 TagCondition.tag_values。
    • 是否存在单引号 TagCondition.tag_code。
    • 是否有多值,( , , , ) TagCondition.tag_values.size。
  • 操作符(=,<)
    • 把英文转为操作符,eq ==》 =,it ==》 < TagCondition.operator。
  • 业务日期
    • 业务日期从页面提取 没有现成的,需要从外部传进来。

完整SQL查询的拼接为bitmapAnd( bitmapAnd( bitmapAnd(sql1,sql2),sql3),sql4)

(2)实现思路

将所有的标签定义全部查找出来,然后放入到堆内存(变量)map中,如果是List则需要根据tag_code依次去List中进行遍历,时间复杂度为O(N)。map中的K存储tag_code,V存储tag_info。

(3)实现过程

controller层

增加方法

/**
 * 生成分群列表
 * @param userGroup
 * @return
 */
@PostMapping("/user-group")
public String genUserGroup(@RequestBody UserGroup userGroup){
    userGroupService.genUserGroup(userGroup);
    return "success";
}

service层

在接口中增加方法

public interface UserGroupService  extends IService<UserGroup> {

    public void genUserGroup(UserGroup userGroup);
}

在实现类中编写

@Service
@Slf4j
@DS("mysql")
public class UserGroupServiceImpl extends ServiceImpl<UserGroupMapper, UserGroup> implements UserGroupService {

    // 以获取全部tagInfo标签的map
    @Autowired
    TagInfoService tagInfoService;

    @Override
    public void genUserGroup(UserGroup userGroup){

        // 1 写入mysql人群的基本定义
        // (1)补充condition_json_str空白字段
        List<TagCondition> tagConditions = userGroup.getTagConditions();
        // 转成json串
        String conditionJson = JSON.toJSONString(tagConditions);
        // 再放回数据库中
        userGroup.setConditionJsonStr(conditionJson);
        // (2)补充condition_commen空白字段,将json串转成一个json说明
        userGroup.setConditionComment(userGroup.conditionJsonToComment());
        // (3)补充create_time空白字段
        userGroup.setCreateTime(new Date());
        // (4)user_group_num和update_time先不处理
        // 调用父类方法
        super.saveOrUpdate(userGroup);
        // 2 写入ClickHouse人群包
        // 全部条件都存储在列表中,一个条件对应list中的一个
        // 所以想生成一个子查询,依据就是集合中的一个condition
        // 获取全部tagInfo标签的map,在TaskInfoServiceImpl中实现
        // K为TagCode,V为TagInfo
        // (1)组合查询Bitmap表完整SQL
        Map<String, TagInfo> tagInfoMapWithCode = tagInfoService.getTagInfoMapWithCode();
        String bitAndSQL = getBitAndSQL(userGroup.getTagConditions(),tagInfoMapWithCode,userGroup.getBusiDate());
        System.out.println(bitAndSQL);

        // 3 人群包(包含所有uid)以应对高QPS访问
        // redis(bitmap/set)
    }

    // 获取完整bitAndSQL语句
    public String getBitAndSQL(List<TagCondition> tagConditionList,Map<String,TagInfo> tagInfoMap,String busiDate){
        String sql="";
        for (TagCondition tagCondition : tagConditionList) {
            String conditionSQL = getConditionSQL(tagCondition,tagInfoMap,busiDate);
            if(sql.length() == 0){
                sql = conditionSQL;
            }else{
                sql = " bitmapAnd(" + sql + "," + conditionSQL + ")";
            }
        }
        return "select " + sql;
    }

    // 2.1 对应2 获取单个condition,生成子查询
    public String getConditionSQL(TagCondition tagCondition,Map<String,TagInfo> tagInfoMap,String busiDate){
        // (1) 获取K,根据K找V
        TagInfo tagInfo = tagInfoMap.get(tagCondition.getTagCode());
        String tagValueType = tagInfo.getTagValueType();
        // (2) 获取表名
        String tableName = null;
        if(tagValueType.equals(ConstCodes.TAG_VALUE_TYPE_STRING)){
            tableName = "user_tag_value_string";
        } else if(tagValueType.equals(ConstCodes.TAG_VALUE_TYPE_LONG)){
            tableName = "user_tag_value_long";
        } else if(tagValueType.equals(ConstCodes.TAG_VALUE_TYPE_DECIMAL)){
            tableName = "user_tag_value_decimal";
        } else if(tagValueType.equals(ConstCodes.TAG_VALUE_TYPE_DATE)){
            tableName = "user_tag_value_date";
        }
        // (3)获取值类型
        String tagValueSQL = getTagValueSQL(tagCondition.getTagValues(), tagValueType);
        // (4)将操作符转换
        String operatorSQL = getConditionOperator(tagCondition.getOperator());
        // (5)拼接SQL
        String conditionSQL = "(select groupBitmapMergeState(us) from " + tableName +
                " where tag_code='" + tagCondition.getTagCode() +
                "' and tag_value " + operatorSQL + " " + tagValueSQL + " " +
                "and dt = '" + busiDate + "')";

        return conditionSQL;
    }

    // 2.2 对应2.1 中(3)
    // 获取SQL中的值部分
    public String getTagValueSQL(List<String> valueList,String tagValueType){
        // 判断是否要单引,是否为多值
        // ('90后','80后','70后')
        boolean needQuote = needStringQuote(tagValueType);
        String tagValueSQL = null;
        if(needQuote){
            tagValueSQL =  " '" + StringUtils.join(valueList,"','") + "' ";
        }else{
            tagValueSQL =  StringUtils.join(valueList,",");
        }
        if(valueList.size() > 1){
            tagValueSQL = "(" + tagValueSQL + ")";
        }
        return tagValueSQL;
    }

    // 2.3 对应2.2
    // 判断是否需要加单引
    public boolean needStringQuote(String tagValueType){
        if(tagValueType.equals(ConstCodes.TAG_VALUE_TYPE_LONG) || tagValueType.equals(ConstCodes.TAG_VALUE_TYPE_DECIMAL)){
            return false;
        } else {
            return true;
        }
    }

    // 对应2.1中(4) 把中文的操作代码转换为判断符号
    private  String getConditionOperator(String operator){
        switch (operator){
            case "eq":
                return "=";
            case "lte":
                return "<=";
            case "gte":
                return ">=";
            case "lt":
                return "<";
            case "gt":
                return ">";
            case "neq":
                return "<>";
            case "in":
                return "in";
            case "nin":
                return "not in";
        }
        throw  new RuntimeException("操作符不正确");
    }
}

Taginfo实现类

使用下面方法得到service中的map

public Map<String,TagInfo> getTagInfoMapWithCode(){
    // 获取全部list
    List<TagInfo> tagInfoList =  super.list();
    Map<String,TagInfo> tagInfoMap=new HashMap<>();
    // 用循环将list转成map
    for (TagInfo tagInfo : tagInfoList) {
        tagInfoMap.put(tagInfo.getTagCode(),tagInfo);
    }
    return tagInfoMap;
}

mapper层

@DS("mysql")
@Mapper
public interface UserGroupMapper extends BaseMapper<UserGroup> {
}

2 人群包代码实现

目的是查出此分群包中包含什么人,人群包的唯一标识是分群ID,所以人群包建表需要内容如下:

  • 包含字段
    • 分群id
    • 分群uid集合,可以直接存储为bitmap
  • 引擎:AggregatingMergeTree
  • 分区:使用分群id进行分区,这样一个分群一个分区,目的是当某个分群的集合发生了变化,造成分区的重建时,不会影响其他的分群
  • 排序:分群id

建表语句如下:

CREATE TABLE  user_group
(
    `user_group_id` String,
    `us` AggregateFunction(groupBitmap, UInt64)
)
ENGINE = AggregatingMergeTree
PARTITION BY user_group_id
ORDER BY user_group_id;

(1)配置文件

spring.datasource.dynamic.datasource.clickhouse.url=jdbc:clickhouse://hadoop101:8123/user_profile1009
spring.datasource.dynamic.datasource.clickhouse.driver-class-name=ru.yandex.clickhouse.ClickHouseDriver

(2)UserGroupMapper

@Insert("${sql}")
@DS("clickhouse")
public void insertSQL(String insertBitSQL);

@Select("select bitmapCardinality(us) ct from user_group where user_group_id=#{userGroupId}")
@DS("clickhouse")
public Long userGroupCount(@Param("userGroupId") String userGroupId);

(3)UserGroupServiceImpl实现类

添加方法

// 从已有人群包中,查询人群个数 对应2 (4) a)
public Long getUserGroupCount(String userGroupId){
    Long userGroupCount = super.baseMapper.userGroupCount(userGroupId);
    return userGroupCount;
}

// 获得插入的SQL语句 对应2 (3)
public  String getInsertBitSQL(String userGroupId,String bitAndSQL){
    String insertSQL="insert into user_group  select '"+userGroupId+"' ,"+bitAndSQL+" as  us";
    return insertSQL;
}

进行调用

//(3)insert into  + 查SQL
String insertBitSQL = getInsertBitSQL(userGroup.getId().toString(), bitAndSQL);

// 执行语句
// 父类已经装配好了mapper 直接使用即可
super.baseMapper.insertSQL(insertBitSQL);

// (4)更新人群包的人数
// a)从已有人群包中,查询人群个数
Long userGroupCount = getUserGroupCount(userGroup.getId().toString());

// b)更新MySQL分群基本信息中的人数,使用mabatis-plus方法
userGroup.setUserGroupNum(userGroupCount);
super.saveOrUpdate(userGroup);

二 预估分群人数

预估分群人群思路如下图:

在这里插入图片描述

三 更新分群人数

更新分群人数思路如下图:

在这里插入图片描述

四 NoSQL数据库

1 NoSQL数据库简介

(1)概述

NoSQL(NoSQL = Not Only SQL ),意即 ”不仅仅是SQL“,泛指非关系型的数据库

NoSQL 不拘泥于关系型数据库的设计范式,放弃了通用的技术标准,为某一领域特定场景而设计,从而使性能、容量或者扩展性都打到了一定程度的突破。

  • 不遵循SQL标准。

    是一个巨大的牺牲,学习成本,人力成本很高,所有的语法都需要学习。

  • 不支持ACID(事务)。

  • 远超于SQL的性能。

(2)NoSQL适用场景

性能快,容量大,扩展性高。

(3)NoSQL不适用场景

需要事务支持、基于sql的结构化查询存储,处理复杂的关系,需要即席查询、用不着sql的和用了sql也不行的情况,请考虑用NoSql。

2 NoSQL家族

(1)Memcache(内存数据库)

很早出现的NoSql数据库。

数据都在内存中,一般不持久化。

持久化:即备份写磁盘。

支持简单的key-value模式,支持类型单一。

KV模式中的K一定是String,类型单一指的是V,Memcache只有String,即一个String对应一个String,一个K只能存储一个值。

不过通过一些转换操作,一个K也可以存储多个值,如将集合变为字符串(JSON)。

一般是作为缓存数据库辅助持久化的数据库。

(2)Redis(内存数据库)

几乎覆盖了Memcached的绝大部分功能。

数据都在内存中,支持持久化,主要用作备份恢复。

MySQL和Hbase都支持写磁盘且使用内存,那么Redis和这两者的区别是什么?

当内存和磁盘中的数据一致时,分不清谁是内存数据库谁是磁盘数据库,当内存和磁盘中的数据不一致时,磁盘数据库以磁盘中的数据为主,这种数据库称为标准的持久化数据库,而内存数据库则以内存中的数据为主,即使是内存数据少,磁盘数据多的情况,如刚刚执行删除操作,删除操作还没有刷到磁盘,磁盘多是作为备份恢复的作用,只有当内存不能使用时,如宕机,才会使用磁盘。

内存数据库中内存的数据是最准确的,任何数据的访问请求都不会第一时间去查磁盘或者写磁盘,会进行周期型的备份。

如果在内存中找不到相应的数据,MySQL和Hbase会查磁盘,而Redis不会去查磁盘,直接返回异常信息。

除了支持简单的key-value模式,还支持多种数据结构的存储,比如 list、set、hash、zset等。

一个K可以存储多个值。

一般是作为缓存数据库辅助持久化的数据库。

一般都是将数据存放到MySQL中,使用Redis作为缓存,其中新浪微博将所有的数据全部存放到Redis中,读写性能大大提高,代价就是成本较高。

(3)Mongodb(文档数据库)

对大数据领域来说,此数据库大多作为一个数据源。

存储的数据结构一般为K-JSON结构,存储结构相对简单,三范式的数据就相对复杂了,表会非常多,Mongodb中的表很少去和其他表进行关联,数据量非常大,不强调关系,可能存储用户与客户的聊天记录、社交中的问答,评论等。

速度介于MySQL和Redis之间。

高性能、开源、模式自由(schema free)的文档型数据库。

数据都在内存中, 如果内存不足,把不常用的数据保存到硬盘。

虽然是key-value模式,但是对value(尤其是json)提供了丰富的查询功能。

支持二进制数据及大型对象。

可以根据数据的特点替代RDBMS ,成为独立的数据库。或者配合RDBMS,存储特定的数据。

(4)Hbase(列式数据库)

HBase是Hadoop项目中的数据库。它用于需要对大量的数据进行随机、实时的读写操作的场景中。

HBase的目标就是处理数据量非常庞大的表,可以用普通的计算机处理超过10****亿行数据,还可处理有数百万**列元素的数据表。

(5)Cassandra(列式数据库)

Apache Cassandra是一款免费的开源NoSQL数据库,其设计目的在于管理由大量商用服务器构建起来的庞大集群上的海量数据集(数据量通常达到PB级别)。在众多显著特性当中,Cassandra最为卓越的长处是对写入及读取操作进行规模调整,而且其不强调主集群的设计思路能够以相对直观的方式简化各集群的创建与扩展流程。

3 DB-Engines数据库排名

国外数据库排行榜

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/11097.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

一个注解干翻所有Controller

1. 概览 日常开发中&#xff0c;最繁琐的便是编写 Controller。很多公司都制定了规范&#xff1a;Controller 不能存在任何的业务逻辑&#xff0c;主要完成参数解析和结果转换。不过查看项目源码&#xff0c;你会发现 Controller 中存在了大量不该存在的逻辑&#xff0c;对此&…

【MFC】打砖块小游戏(下)(7)

任务点&#xff1a; 1、键盘左右键消息处理&#xff1b; 2、碰撞检测&#xff08;与砖块、挡板、上、左、右&#xff09;&#xff1b; 3、控制转向&#xff1b; 程序shix 解决思路&#xff1a; 1、左右键消息处理&#xff1a; 响应 WM_KEYDOWN 消息&#xff0c;移动挡板…

【Spring】——1、使用@Configuration和@Bean给容器中注册组件

&#x1f4eb;作者简介&#xff1a;zhz小白 公众号&#xff1a;小白的Java进阶之路 专业技能&#xff1a; 1、Java基础&#xff0c;并精通多线程的开发&#xff0c;熟悉JVM原理 2、熟悉Java基础&#xff0c;并精通多线程的开发&#xff0c;熟悉JVM原理&#xff0c;具备⼀定的线…

ArrayList详解

ArrayList是什么? ArrayList就是动态数组&#xff0c;是List接口的可调整大小的数组实现&#xff1b;除了实现List接口之外&#xff0c;该类还提供了一些方法来操纵内部使用的存储列表的数组大小。它的主要底层实现是数组Object[] elementData。 为什么要设计ArrayList&…

【C++】mapset利用红黑树进行简单封装

前言 大家好~~~~呀&#xff01;很荣幸你能点击这篇文章。本篇也是我的一份学习笔记&#xff0c;让我们一起共同成长吧~ing...... C红黑树的简单插入实现博客~ 【C】红黑树的插入实现_柒海啦的博客-CSDN博客 二叉搜索树的基本结构和实现博客~ 【C】二叉搜索树_柒海啦的博客-CSDN…

java 实现一个最小栈

文章目录最小栈1.实现思路2.实现过程演示3.代码实现思路3.1 压入思路3.2 弹出思路3.3 如何返回栈顶元素的下标3.4 如何返回栈的最小值4.整体代码实现最小栈 1.实现思路 实现一个stack栈 和 minStack栈。先将数据一个一个压入到 stack 中。找到 stack 中的最小值。minStack中始…

简单介绍动态链接过程

文章目录gotgot[0] link_map结构体地址got[1] _dl_runtime_resolvegot[2]之后pltplt[0] 调用libc解析函数plt后面的plt.sec随便拿ida打开一个程序可以看到这是got的内容gdb一下查看内容&#xff0c;可以看到地址是从0开始的大家也知道 got是个独立的section&#xff0c;所以最开…

MySQL数据库(Java的数据库编程:JDBC)

作者&#xff1a;渴望力量的土狗 博客主页&#xff1a;渴望力量的土狗的博客主页 专栏&#xff1a;MySQL数据库 目录 什么是数据库编程&#xff1a; 什么是JDBC? JDBC工作原理&#xff1a; JDBC的使用及相关操作&#xff1a; JDBC开发案例&#xff1a; JDBC常用接口…

关于电影的HTML网页设计-威海影视网站首页-电影主题HTM5网页设计作业成品

HTML实例网页代码, 本实例适合于初学HTML的同学。该实例里面有设置了css的样式设置&#xff0c;有div的样式格局&#xff0c;这个实例比较全面&#xff0c;有助于同学的学习,本文将介绍如何通过从头开始设计个人网站并将其转换为代码的过程来实践设计。 文章目录一、网页介绍一…

5分钟教你如何设计一个安全web架构

今天就给大家聊聊web安全&#xff0c;web安全占比还是比较大的&#xff0c;基础的从一些html标签&#xff0c;到js 然后到接口&#xff0c;数据库&#xff0c;以及流量攻击&#xff0c;模拟请求。当然这也谈到了一个概念&#xff0c;全新的架构设计模式&#xff0c;前后端分离&…

一文讲解如何学习 Linux 内核网络协议栈

协议栈的细节 下面将介绍一些内核网络协议栈中常常涉及到的概念。 sk_buff 内核显然需要一个数据结构来表示报文&#xff0c;这个结构就是 sk_buff ( socket buffer 的简称)&#xff0c;它等同于在<TCP/IP详解 卷2>中描述的 BSD 内核中的 mbuf。 sk_buff 结构自身并不…

【毕业设计】深度学习人脸性别年龄识别系统 - python

文章目录0 前言1 课题描述2 实现效果3 算法实现原理3.1 数据集3.2 深度学习识别算法3.3 特征提取主干网络3.4 总体实现流程4 具体实现4.1 预训练数据格式4.2 部分实现代码5 最后0 前言 &#x1f525; Hi&#xff0c;大家好&#xff0c;这里是丹成学长的毕设系列文章&#xff0…

【案例分享】华为防火墙出接口方式的单服务器智能DNS配置

介绍出接口方式的单服务器智能DNS的配置举例。 组网需求 如图1所示&#xff0c;企业部署了一台ISP1服务器对外提供Web服务&#xff0c;域名为www.example.com。ISP1服务器的私网IP地址为10.1.1.10&#xff0c;服务器映射后的公网IP地址为1.1.1.10。企业的DNS服务器上存在域名w…

为什么你的用户转化率不高?-- 新媒体运营转化效果渠道归因分析

新媒体运营人最关注的就是流量和用户转化问题。公司发布了新APP、上线了新网站项目&#xff0c;进行用户定位、策划、数据分析和内容营销&#xff0c;花重钱做产品推广&#xff0c;但最后用户转化率却不高&#xff0c;大批用户流失了......这种现象是运营人最不愿意看到的&…

老杨说运维|今年这个会议非比寻常

前言&#xff1a; 人民银行印发的《金融科技(FinTech)发展规划(2022-2025年)》中&#xff0c;重点围绕数字化转型建设&#xff0c;强调上云、数据基础建设以及数智应用的重要性&#xff0c;明确了金融科技的长期重点建设方向。 由金科创新社主办的“2022金融业新一代数据中心发…

kwebio/kweb-core:面向后端的轻量级 Kotlin Web 框架

现代网站至少由两个紧密耦合 的组件组成&#xff0c;一个在浏览器中运行&#xff0c;另一个在服务器上。它们通常用不同的编程语言编写&#xff0c;并且必须通过 HTTP(S) 连接相互通信。 Kweb 的目标是消除这种服务器/浏览器分离&#xff0c;这样您就可以专注于构建您的网站或用…

react多组件出错其他正常显示

问题&#xff1a;一个组件内部有很多个子组件&#xff0c;其中一个出错&#xff0c;怎么实现其他组件可以正常显示&#xff0c;而不是页面挂掉&#xff1f; 一、错误边界 可以捕获发生在其子组件树任何位置的 JavaScript 错误&#xff0c;并打印这些错误&#xff0c;同时展示…

CC攻击和DDOS攻击哪个对服务器影响更大

互联网企业&#xff0c;不管是小企业&#xff0c;还是大企业&#xff0c;大多数企业网站都遭受过攻击&#xff0c;而我们时不时的也能在网上看见某大型企业网站被攻击&#xff0c;崩溃的新闻&#xff0c;网络攻击可以说是屡见不鲜了。攻击力最常见的就是DDOS攻击和CC攻击&#…

使用HTML+CSS技术制作篮球明星介绍网站

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

2022年数维杯国际数模赛浅评

今日数维杯国际大学生数学建模挑战赛将要开赛&#xff0c;为了更好的帮助大家整理了以下注意事项&#xff0c; 竞赛开始时间&#xff1a;北京时间2022年11月17日08:00&#xff08;周四&#xff09; 竞赛结束时间&#xff1a;北京时间2022年11月21日08&#xff1a;00&#xff…