日新增百万数据clickhouse大数据解决方案记录分享

公司广告业务需求,需要多个维度统计每个应用的设备数,点击率,展示率,等相关数据,而且数据需要进行去重,我第一时间想到的是利用clickhouse来做统计,因为我们平台访问量比较大,用mysql可能不太适合
首先我建了四个表

#点击数据表
CREATE TABLE raw_click
(`Date` Date,`Time` DateTime,`Hour` Int8,`AdvertiserID` UInt32 DEFAULT 0,`AdsID` UInt32 DEFAULT 0,`DeveloperID` UInt32 DEFAULT 0,`WebID` UInt32 DEFAULT 0,`FeeTypeID` UInt32 DEFAULT 0,`AdvType` UInt8 DEFAULT 0,`GroupID` UInt32 DEFAULT 0,`PlatformID` UInt32 DEFAULT 0,`PlatformNameID` UInt8 DEFAULT 0,`MaterialId` UInt32 DEFAULT 0,`DeviceID` Nullable(String) DEFAULT NULL,`AppOs` UInt8 DEFAULT 1
)
ENGINE = MergeTree
PARTITION BY toYYYYMMDD(Date)
ORDER BY Date
SETTINGS index_granularity = 8192#填充数表
CREATE TABLE raw_fill
(`Date` Date,`Time` DateTime,`Hour` Int8,`AdvertiserID` UInt32 DEFAULT 0,`AdsID` UInt32 DEFAULT 0,`DeveloperID` UInt32 DEFAULT 0,`WebID` UInt32 DEFAULT 0,`FeeTypeID` UInt32 DEFAULT 0,`AdvType` UInt8 DEFAULT 0,`GroupID` UInt32 DEFAULT 0,`PlatformID` UInt32 DEFAULT 0,`PlatformNameID` UInt8 DEFAULT 0,`MaterialId` UInt32 DEFAULT 0,`DeviceID` Nullable(String) DEFAULT NULL,`AppOs` UInt8 DEFAULT 1
)
ENGINE = MergeTree
PARTITION BY toYYYYMMDD(Date)
ORDER BY Date
SETTINGS index_granularity = 8192#请求数表
CREATE TABLE raw_request
(`Date` Date,`Time` DateTime,`Hour` Int8,`AdvertiserID` UInt32 DEFAULT 0,`AdsID` UInt32 DEFAULT 0,`DeveloperID` UInt32 DEFAULT 0,`WebID` UInt32 DEFAULT 0,`FeeTypeID` UInt32 DEFAULT 0,`AdvType` UInt8 DEFAULT 0,`GroupID` UInt32 DEFAULT 0,`PlatformID` UInt32 DEFAULT 0,`PlatformNameID` UInt8 DEFAULT 0,`MaterialId` UInt32 DEFAULT 0,`DeviceID` Nullable(String) DEFAULT NULL,`AppOs` UInt8 DEFAULT 1
)
ENGINE = MergeTree
PARTITION BY toYYYYMMDD(Date)
ORDER BY Date
SETTINGS index_granularity = 8192#展示数表
CREATE TABLE raw_show
(`Date` Date,`Time` DateTime,`Hour` Int8,`AdvertiserID` UInt32 DEFAULT 0,`AdsID` UInt32 DEFAULT 0,`DeveloperID` UInt32 DEFAULT 0,`WebID` UInt32 DEFAULT 0,`FeeTypeID` UInt32 DEFAULT 0,`AdvType` UInt8 DEFAULT 0,`GroupID` UInt32 DEFAULT 0,`PlatformID` UInt32 DEFAULT 0,`PlatformNameID` UInt8 DEFAULT 0,`MaterialId` UInt32 DEFAULT 0,`DeviceID` Nullable(String) DEFAULT NULL,`AppOs` UInt8 DEFAULT 1
)
ENGINE = MergeTree
PARTITION BY toYYYYMMDD(Date)
ORDER BY Date
SETTINGS index_granularity = 8192

当时建表时,我犹豫了两个方面,第一需不需要进行按月分表,然后我问了chatgpt在这里插入图片描述
翻译过来的意思就是《你硬件的极限才是我clickhouse的极限》,那我就放心把数据往里面塞了
犹豫的第二点就是,我要不要只建一个表,将点击展示填充这些行为用type区分。后来仔细思考了一下,还是觉得每个行为进行一次分表是最好的
数据表里的每个字段,都将是我们业务报表,需要进行维度查询的条件,所以数据库就这样定下来了。
接下来就是需要考虑怎么将数据插入进来,我这里只分享一下我的插入数据脚本

#!/usr/local/php/bin/php -q
<?php
declare(ticks=1);const _TOUCHER_NAME_ = "ch_stat";#同步器的名称// 如果存在开发环境配置,则加载
include("int/clickhouse1.3.10/Clickhouse.php");
include("int/config.php");$mq_name = $argv[1] ?? '';
if (empty($mq_name)) {exit("不是正确的打开方式!");
}
$table_name_arr = ['raw_show_mq' => 'raw_show','raw_click_mq' => 'raw_click','raw_fill_mq' => 'raw_fill','raw_request_mq' => 'raw_request'
];
$table_name = $table_name_arr[$mq_name] ?? '';
if (empty($table_name)) {exit("不是正确的打开方式啊!");
}
#监听断开信号
$handle = true;
pcntl_signal(SIGTERM, 'handleSignal');
pcntl_signal(SIGINT, 'handleSignal');
pcntl_signal(SIGQUIT, 'handleSignal');#链接redis
$redisconn = redis_conn();
$redisconn->select(9);$clickhouse = new Clickhouse($ch_config, '数据库表名');while (true) {if (date("H") == '05' && date("i") == '00' && date("s") == '00') {exit(_TOUCHER_NAME_ . ":I am gone away");}$start_time = microtime_float();  //记录开始时间try {$queueLen = $redisconn->lLen($mq_name);} catch (\Exception $e) {# 预防redis 挂掉exit(_TOUCHER_NAME_ . ": redis gone away ");}#暂时一次插入1000$queue_count = 1000;$data = [];if ($queueLen < $queue_count) {#数据不够 我在等等$queue_count = $queueLen;
//        msg2log(_TOUCHER_NAME_ . ":数据不够 我在等等!");
//        sleep(3);
//        continue;}for ($i = 0; $i < $queue_count; $i++) {#取出队列的数据$json_data = $redisconn->rPop($mq_name);if (empty($json_data)) {#会有为空吗continue;}#组装数据插入$data[] = json_decode($json_data, true);}if (empty($data)) {msg2log(_TOUCHER_NAME_ . ":队列暂时没有可消耗数据!");sleep(5);continue;}#批量插入try {$clickhouse->insert($table_name, $data);} catch (Exception $exception) {#批量插入失败 全部推回去msg2log(_TOUCHER_NAME_ . ":批量插入失败,将数据推回去");foreach ($data as $v) {#数据结构有问题 可暂时先注释$redisconn->lPush($mq_name, json_encode($v));}#清空数据$data = [];#排除是不是clickhouse挂了if (!$clickhouse->alive()) {exit("clickhouse 链接异常 尝试退出重连!");}}$end_time = microtime_float();if (!$handle) {msg2log(_TOUCHER_NAME_ . ":程序主动退出!Using Time " . ($end_time - $start_time) . " Sec, Totoal touched :" . count($data));break;}msg2log(_TOUCHER_NAME_ . ": Using Time " . ($end_time - $start_time) . " Sec, Totoal touched :" . count($data));sleep(3);
}function handleSignal($signal)
{global $handle;switch ($signal) {case SIGTERM:case SIGINT:case SIGQUIT:$handle = false;#exit;// 处理其他信号...}
}?>

脚本的内容,主要就是从队列里面拿到数据插入到clickhouse里面去,然后里面加了一点检测redis,clickhouse是否断开的判断处理,以及当数据存在异常时,将数组从新推回队列,防止数据丢失,最后一点就是当我们断掉脚本的时候,检测信号,将数据整理完毕之后再断开,这样尽可能的避免数据的丢失
在这里插入图片描述
插入数据脚本没问题了之后,等到数据进来,发现数据增长的是真的快,这是跑了2个多月的数据,因为平台流量大,导致数据很多,虽然查询起来有没有问题,但是我发现每次执行sql,时间大约在一个四五秒左右(以下面这段sql为例)

SELECT Date, SUM(dau) AS dau, SUM(request) AS request, SUM(fill) AS fill, SUM(show) AS show, SUM(click) AS click 
FROM (SELECT Date, count(distinct DeviceID) AS dau, count(*) AS request, 0 AS fill, 0 AS show, 0 AS click FROM raw_request WHERE PlatformNameID > 0 AND Date BETWEEN '2024-03-07' AND '2024-03-13' GROUP BY DateUNION ALLSELECT Date, 0 AS dau, 0 AS request, count(*) AS fill, 0 AS show, 0 AS click FROM raw_fill WHERE PlatformNameID > 0 AND Date BETWEEN '2024-03-07' AND '2024-03-13' GROUP BY DateUNION ALLSELECT Date, 0 AS dau, 0 AS request, 0 AS fill, count(*) AS show, 0 AS click FROM raw_show WHERE PlatformNameID > 0 AND Date BETWEEN '2024-03-07' AND '2024-03-13' GROUP BY DateUNION ALLSELECT Date, 0 AS dau, 0 AS request, 0 AS fill, 0 AS show, count(*) AS click FROM raw_click WHERE PlatformNameID > 0 AND Date BETWEEN '2024-03-07' AND '2024-03-13' GROUP BY Date
) AS subquery
GROUP BY Date
ORDER BY Date DESC;

后面我发现其实,之前的历史数据,基本上都用不到,另外一直存着这些数据,备份起来,担心磁盘不够用,所以我想着只保存前面一个月的数据,因为我的数据存储是按天分区的,所以我删除的时候也要按天来删,注意删之后一定要归档一份,删除语句主要用到的是
ALTER TABLE table DROP PARTITION date
日期的格式是20240303 这样的,删除之后,发现数据查询确实也是会快一点,后面再慢慢优化

ALTER TABLE table ADD column 字段名 UInt8 DEFAULT 默认值;

clickhouse目的是为了存储更多的信息,尽量扩展到每一个我们可能会用到的查询条件,如果忘记了,那么我们就需要新增字段,新增字段还是比较快的,上亿条数据执行这段sql,一秒不到,个人猜测可能跟他的列式存储方式有关
最后,这是我个人的一个经验分享,欢迎大家交流学习,也希望能对你有帮助。

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

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

相关文章

浅谈WPF之MVVM工具包

在之前的WPF示例中&#xff0c;都会用到一个MVVM框&#xff0c;也是一个比较常的MVVM框架&#xff0c;就是MVVM工具包【CommunityToolkit.Mvvm】&#xff0c;今天专门以一个简单的小例子&#xff0c;简述一下MVVM工具包的常见用法&#xff0c;仅供学习分享使用&#xff0c;如有…

选项式API和组合式API的区别

选项式(options) API 和组合式(composition) API两种不同的风格书写&#xff0c;Vue3 的组件可以使用这两种api来编写。 选项式API和组合式API的区别 选项式API 选项式 API&#xff0c;具有相同功能的放在一起&#xff0c;可以用包含多个选项的对象来描述组件的逻辑&…

WPF---1.入门学习

学习来源 布局 wpf布局原则 一个窗口中只能包含一个元素 不应显示设置元素尺寸 不应使用坐标设置元素的位置 可以嵌套布局容器 StackPanel-->表单条件查找布局 DataGrid wpf布局容器 StackPanel: 水平或垂直排列元素&#xff0c;Orientation属性分别: Horizontal / Vertic…

Java数据结构-ArrayList

目录 1. 初识集合框架2. ArrayList的介绍3. ArrayList的使用3.1 构造方法3.2 add3.3 addAll3.4 remove3.5 get3.6 set3.7 contains3.8 IndexOf3.9 lastIndexOf3.10 subList 4. ArrayList的遍历4.1 简单粗暴法4.2 循环遍历法4.3 迭代器 1. 初识集合框架 Java集合框架是Java编程…

基于OneAPI+ChatGLM3-6B+FastGPT搭建LLM大语言模型知识库问答系统

搭建大语言模型知识库问答系统 部署OneAPI部署一个LLM模型部署嵌入模型部署FastGPT新建FastGPT对话应用新建 FastGPT 知识库应用 部署OneAPI 拉取镜像 docker pull justsong/one-api创建挂载目录 mkdir -p /usr/local/docker/oneapi启动容器 docker run --name one-api -d …

粘包/半包及解决方案

一、粘包/半包介绍 1&#xff1a;粘包 粘包&#xff08;Packet Concatenation&#xff09;通常发生在基于流式传输协议&#xff08;如 TCP&#xff09;的通信中&#xff0c;因为 TCP 是面向流的传输协议&#xff0c;它不保证数据包的边界&#xff0c;而是将数据视为连续的字节…

密码学及其应用1 —— 密码学概述

1 密码学的基本概念 1.1 网络安全的定义 网络安全是网络领域的一个专业领域&#xff0c;它涵盖了在基础计算机网络基础设施中所采取的措施、网络管理员为保护网络及网络可访问资源免受未授权访问而采纳的政策&#xff0c;以及对其有效性&#xff08;或无效性&#xff09;的持续…

2024年2月线上助听器综合电商(京东天猫淘宝)热销排行榜

鲸参谋监测的综合电商平台&#xff08;京东天猫淘宝&#xff09;2月份助听器品牌销量销额排行榜已揭晓&#xff01; 根据鲸参谋电商大数据显示&#xff0c;2月助听器在综合电商平台销量约为19万&#xff0c;环比上个月下滑了2%&#xff0c;同比去年下滑了25%&#xff1b;销售额…

基于nodejs+vue发艺美发店管理系统python-flask-django-php

系统根据现有的管理模块进行开发和扩展&#xff0c;采用面向对象的开发的思想和结构化的开发方法对发艺美发店管理的现状进行系统调查。采用结构化的分析设计&#xff0c;该方法要求结合一定的图表&#xff0c;在模块化的基础上进行系统的开发工作。在设计中采用“自下而上”的…

javaSwing愤怒的小鸟游戏

一、简介 游戏名称是“愤怒的小鸟”&#xff0c;英文称为“AngryBird”。 “愤怒的小鸟”是著名游戏公司Rovio偶然间开发出来的益智游戏&#xff0c;从2009年12月上市到iOS。&#xff0c;讲述了鸟类和猪因为猪偷鸟蛋反生的一系列故事。游戏的类型版本是横向版本的水平视角&…

6、运行时数据区

Java虚拟机在运行Java程序过程中管理的内存区域&#xff0c;称之为运行时数据区。《Java虚拟机规范》中规定了每一部分的作用。 3.1 程序计数器 程序计数器&#xff08;Program Counter Register&#xff09;也叫PC寄存器&#xff0c;每个线程会通过程序计数器记录当前要执行的…

opencv各个模块介绍(1)

Core 模块&#xff1a;核心模块&#xff0c;提供了基本的数据结构和功能。 常用的核心函数&#xff1a; cv::Mat&#xff1a;表示多维数组的数据结构&#xff0c;是OpenCV中最常用的类之一&#xff0c;用于存储图像数据和进行矩阵运算。 cv::Scalar&#xff1a;用于表示多通道…

网络分层协议和应用模型

分层模型 五层网络模型 MAC地址跟IP地址的区别&#xff1a;MAC地址是唯一的&#xff0c;相当于每个人的指纹&#xff0c;出生时就是唯一的&#xff1b;IP地址就相当于是你当前的住址&#xff0c;是会发生变化的&#xff0c;但是是动态唯一的。 应用层协议 URL URL&#xff…

基于Colab训练的yolov4-tiny自定义数据集(可用于OpenCV For Unity)

参考资料文档和视频。 1.打开文档,点击【文件】【在云端硬盘中保存一份副本】,即将文档复制到自己云端硬盘。 2.打开该文件,按文中提示进行。 【代码执行程序】【更改运行时类型】修改运行时为GPU(免费的GPU不好用,收费的好用,某宝上几十元就可用一个月) 步骤1) !git…

如何调用occtproxy放入自己的wpf文件

1.创建一个wpf程序 2.添加项目occtproxy.vcxproj 3.把该项目配置类型设为dll 4.添加引用 5.报错显示&#xff0c;这是因为还没有生成dll 6.把occtproxy设为启动项目运行&#xff0c;设定输出目录在该目录下&#xff0c;生成dll 7.再运行&#xff0c;即可

一文整合工厂模式、模板模式、策略模式

为什么使用设计模式 今天终于有时间系统的整理一下这几个设计模式了&#xff0c; 这几个真是最常用的&#xff0c;用好了它们&#xff0c;你就在也不用一大堆的if else 了。能更好的处理大量的代码冗余问题。 在我们的实际开发中&#xff0c;肯定会有这样的场景&#xff1a;我…

2024年云仓酒庄新动态:铸就新篇章

原标题&#xff1a;刘总出席成都糖酒会&#xff1a;信任铸就云仓酒庄新篇章&#xff0c;共襄盛举展未来近日&#xff0c;备受瞩目的成都糖酒会盛大开幕&#xff0c;吸引了来自全国各地的业界精英和代表。在这场盛大的行业盛会上&#xff0c;云仓酒庄的刘总亲临现场。 现场&…

以XX大学校园为例的智慧能源管理系统建设方案【能源物联网+智能微电网数字校园、节能校园、低碳校园】

建设背景 贯彻落实《中共中央 国务院关于完整准确全面贯彻新发展理念做好碳达峰碳中和工作的意见》和《国务院关于印发2030年前碳达峰行动方案的通知》要求&#xff0c;把绿色低碳发展纳入国民教育体系。 2021年3月26日为推动信息技术与教育教学深度融合&#xff0c;教育部印…

2.6 IDE(集成开发环境)是什么

IDE&#xff08;集成开发环境&#xff09;是什么 IDE 是 Integrated Development Environment 的缩写&#xff0c;中文称为集成开发环境&#xff0c;用来表示辅助程序员开发的应用软件&#xff0c;是它们的一个总称。 通过前面章节的学习我们知道&#xff0c;运行 C 语言&…

eclipse导入svn项目

1、配置maven 2、用svn引入项目 3一直点击next,到最后选完成。