小言_互联网的博客

SpringBoot2.X整合ClickHouse项目实战-从零搭建整合(三)

584人阅读  评论(0)

一、ClickHouse+SpringBoot2.X+MybatisPlus整合搭建

二、需求描述和数据库准备

 三、ClickHouse统计SQL编写实战和函数使用

四、ClickHouse+SpringBoot2.X案例-基础模块搭建 

controller/request层

mapper层 

 model层

 service层

 五、ClickHouse+SpringBoot2.X案例-数据统计接口

service层

mapper层

一、ClickHouse+SpringBoot2.X+MybatisPlus整合搭建

1.在线创建项目 https://start.spring.io/

 idea导入刚下载下来的项目

 在pom.xml中增加ClickHouse依赖


  
  1. <dependency>
  2. <groupId>ru.yandex.clickhouse </groupId>
  3. <artifactId>clickhouse-jdbc </artifactId>
  4. <version>0.1.55 </version>
  5. </dependency>
  6. <!--mybatis plus-->
  7. <dependency>
  8. <groupId>com.baomidou </groupId>
  9. <artifactId>mybatis-plus-boot-starter </artifactId>
  10. <version>3.4.0 </version>
  11. </dependency>

 数据库连接配置


  
  1. server.port = 8080
  2. spring.datasource.driver-class-name =ru.yandex.clickhouse.ClickHouseDriver
  3. spring.datasource.url =jdbc:clickhouse: / / 11x.xxx.xx. 24x: 8123 / default
  4. mybatis-plus. configuration.log-impl =org.apache.ibatis.logging.stdout.StdOutImpl
  5. logging.level.root =INFO

二、需求描述和数据库准备

指定商品,统计指定日期范围内,各个省市的访问的pv数量,如7天内有各个城市访问某个商品分布

指定商品,多天内的访问曲线变化图,如,1~7号商品访问量波动图

建表语句:


  
  1. CREATE TABLE default.visit_stats
  2. (
  3. `product_id` UInt64, / /商品id
  4. `is_new` UInt16, / /是否是新用户 1新用户 0老用户
  5. `province` String, / /省名称
  6. `city` String, / /市名称
  7. `pv` UInt32, / /轻度聚合后的访问量
  8. `visit_time` DateTime / /访问时间
  9. )
  10. ENGINE = MergeTree()
  11. PARTITION BY toYYYYMMDD(visit_time)
  12. ORDER BY (
  13. product_id,
  14. is_new,
  15. province,
  16. city
  17. );

插入sql:


  
  1. INSERT into visit_stats values
  2. ( '1', '1', '广东', '广州', 14323, '2023-01-01 12:11:13'),
  3. ( '1', '0', '广东', '广州', 4232, '2023-02-12 16:16:13'),
  4. ( '1', '1', '广东', '佛山', 54323, '2023-03-06 16:11:13'),
  5. ( '1', '0', '广东', '东莞', 42341, '2023-03-02 16:12:13'),
  6. ( '1', '1', '广东', '梅州', 52422, '2023-03-09 12:11:13'),
  7. ( '2', '1', '广东', '广州', 14323, '2021-03-01 12:11:13'),
  8. ( '2', '0', '广东', '深圳', 425232, '2023-04-12 16:16:13'),
  9. ( '2', '1', '广东', '佛山', 543323, '2022-06-06 16:11:13'),
  10. ( '2', '0', '广东', '东莞', 42341, '2021-05-02 16:12:13'),
  11. ( '2', '1', '广东', '梅州', 52422, '2022-01-09 12:11:13'),
  12. ( '3', '1', '北京', '北京', 13132, '2023-01-01 12:11:13'),
  13. ( '3', '0', '广东', '广州', 533232, '2022-02-16 16:16:13'),
  14. ( '4', '1', '浙江', '杭州', 663643, '2023-12-06 12:11:13'),
  15. ( '4', '0', '广东', '东莞', 4142, '2023-11-02 16:12:13'),
  16. ( '5', '1', '湖南', '长沙', 52123, '2022-01-09 12:11:13'),
  17. ( '4', '0', '湖南', '衡阳', 4142, '2024-05-02 16:12:13'),
  18. ( '5', '1', '广东', '中山', 52123, '2024-01-09 12:11:13'),
  19. ( '2', '1', '上海', '上海', 14323, '2021-03-01 12:11:13'),
  20. ( '5', '0', '浙江', '宁波', 425232, '2023-04-12 16:16:13'),
  21. ( '3', '1', '广东', '佛山', 543323, '2022-06-06 16:11:13'),
  22. ( '2', '0', '湖南', '长沙', 42341, '2021-05-02 16:12:13'),
  23. ( '2', '1', '广东', '深圳', 52422, '2022-01-09 12:11:13')

 三、ClickHouse统计SQL编写实战和函数使用

统计需求:某个商品再时间范围内地区访问分布-城市级别,天级别


  
  1. select province,city, sum(pv) pv_count
  2. from visit_stats where product_id = 1
  3. and toYYYYMMDD(visit_time) BETWEEN '20200101' and '20241212'
  4. group by province,city order by pv_count desc

函数:
求和:sum(pv) 
年格式:select toYear(toDateTime('2024-12-11 11:12:13')) 
日期格式化:select toYYYYMMDD(toDateTime('2024-12-11 11:12:13')) 
日期时间格式化:select toYYYYMMDDhhmmss(toDateTime('2024-12-11 11:12:13')) 
周格式化,1~7,当前时间是本周第几天,下面是周三结果是3,周日结果是7
select toDayOfWeek(toDateTime('2024-12-11 11:12:13')) 

小时格式化,提取时间里面的小时,比如 2023-12-29 10:05:10,格式化后是【10】点
select toHour(toDateTime('2024-12-11 11:12:13')) 

分钟格式化,提取时间里面的分钟,比如 2023-12-29 10:05:10,格式化后是【5】分钟
select toMinute(toDateTime('2024-12-11 11:12:13')) 

秒格式化,提取时间里面的秒
select toSecond(toDateTime('2024-12-11 11:12:13')) 

获取当前日期时间
select now()

获取当前日期
select today()

某个商品,多天内的访问曲线图, 天级别


  
  1. select
  2. toYYYYMMDD(visit_time) date_time_str,
  3. sum(pv) pv_count from visit_stats
  4. where product_id = 2
  5. and toYYYYMMDD(visit_time) BETWEEN '20200101' and '20241212'
  6. group by date_time_str ORDER BY date_time_str desc

 所用函数:

逻辑判断:
SELECT if(cond, then, else)
例子:SELECT if(1, plus(3, 3), plus(6, 8))

如果条件 cond 的计算结果为非零值,则返回表达式 then 的结果,并且跳过表达式 else 的结果
如果 cond 为零或 NULL,则将跳过 then 表达式的结果,并返回 else 表达式的结果

字符串拼接(不能双引号):
select concat('我','上班的时候','没有摸鱼~')


最大、最小、平均值:
select max(pv), min(pv), avg(pv) from visit_stats

四、ClickHouse+SpringBoot2.X案例-基础模块搭建 

controller/request层


  
  1. package net.wnnck.demo.controller.request;
  2. public class VisitRecordPageRequest {
  3. private long productId;
  4. private int page;
  5. private int size;
  6. public long getProductId () {
  7. return productId;
  8. }
  9. public void setProductId (long productId) {
  10. this.productId = productId;
  11. }
  12. public int getPage () {
  13. return page;
  14. }
  15. public void setPage (int page) {
  16. this.page = page;
  17. }
  18. public int getSize () {
  19. return size;
  20. }
  21. public void setSize (int size) {
  22. this.size = size;
  23. }
  24. }
  25. package net.wnnck.demo.controller;
  26. import net.wnnck.demo.controller.request.VisitRecordPageRequest;
  27. import net.wnnck.demo.model.JsonData;
  28. import net.wnnck.demo.service.VisitStatsService;
  29. import org.springframework.beans.factory.annotation.Autowired;
  30. import org.springframework.web.bind.annotation.RequestBody;
  31. import org.springframework.web.bind.annotation.RequestMapping;
  32. import org.springframework.web.bind.annotation.RestController;
  33. import java.util.Map;
  34. @RestController
  35. @RequestMapping("/api/v1/data")
  36. public class DataController {
  37. @Autowired
  38. private VisitStatsService visitStatsService;
  39. @RequestMapping("page")
  40. public JsonData queryVisitRecord (@RequestBody VisitRecordPageRequest pageRequest){
  41. Map<String, Object> map = visitStatsService.pageVisitRecord(pageRequest);
  42. return JsonData.buildSuccess(map);
  43. }
  44. }

mapper层 


  
  1. package net.wnnck.demo.mapper;
  2. import com.baomidou.mybatisplus.core.mapper.BaseMapper;
  3. import net.wnnck.demo.model.VisitStatsDO;
  4. import org.apache.ibatis.annotations.Mapper;
  5. import org.apache.ibatis.annotations.Param;
  6. import java.util.List;
  7. @Mapper
  8. public interface VisitStatsMapper extends BaseMapper<VisitStatsDO> {
  9. /**
  10. * 统计总条数
  11. * @param productId
  12. * @return
  13. */
  14. int countTotal (@Param("productId") long productId);
  15. /**
  16. * 分页
  17. * @param from
  18. * @param size
  19. * @return
  20. */
  21. List<VisitStatsDO> pageVisitRecord (@Param("productId")Long productId , @Param("from") int from, @Param("size") int size);
  22. }
  23. resources/mapper/VisitStatsMapper.xml
  24. <?xml version= "1.0" encoding= "UTF-8"?>
  25. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  26. <mapper namespace= "net.wnnck.demo.mapper.VisitStatsMapper">
  27. <!-- 通用查询映射结果 -->
  28. <resultMap id= "BaseResultMap" type= "net.wnnck.demo.model.VisitStatsDO">
  29. <result column= "product_id" property= "productId"/>
  30. <result column= "is_new" property= "isNew"/>
  31. <result column= "province" property= "province"/>
  32. <result column= "city" property= "city"/>
  33. <result column= "pv" property= "pv"/>
  34. <result column= "visit_time" property= "visitTime"/>
  35. </resultMap>
  36. <!-- 通用查询结果列 -->
  37. <sql id= "Base_Column_List">
  38. product_id,is_new,province,city,pv,visit_time
  39. </sql>
  40. <!--统计总条数-->
  41. <select id= "countTotal" resultType= "java.lang.Integer">
  42. select count (1) from visit_stats where product_id=#{productId}
  43. </select>
  44. <!--分页查找-->
  45. <select id= "pageVisitRecord" resultMap= "BaseResultMap">
  46. select
  47. <include refid= "Base_Column_List"/>
  48. from visit_stats where product_id=#{productId}
  49. order by visit_time desc limit #{from},#{size}
  50. </select>
  51. </mapper>

 model层


  
  1. package net.wnnck.demo.model;
  2. public class JsonData {
  3. /**
  4. * 状态码 0 表示成功
  5. */
  6. private Integer code;
  7. /**
  8. * 数据
  9. */
  10. private Object data;
  11. /**
  12. * 描述
  13. */
  14. private String msg;
  15. public JsonData (){
  16. }
  17. public JsonData (Integer code, Object data, String msg) {
  18. this.code = code;
  19. this.data = data;
  20. this.msg = msg;
  21. }
  22. /**
  23. * 成功,不传入数据
  24. *
  25. * @return
  26. */
  27. public static JsonData buildSuccess () {
  28. return new JsonData( 0, null, null);
  29. }
  30. /**
  31. * 成功,传入数据
  32. *
  33. * @param data
  34. * @return
  35. */
  36. public static JsonData buildSuccess (Object data) {
  37. return new JsonData( 0, data, null);
  38. }
  39. /**
  40. * 失败,传入描述信息
  41. *
  42. * @param msg
  43. * @return
  44. */
  45. public static JsonData buildError (String msg) {
  46. return new JsonData(- 1, null, msg);
  47. }
  48. /**
  49. * 自定义状态码和错误信息
  50. *
  51. * @param code
  52. * @param msg
  53. * @return
  54. */
  55. public static JsonData buildCodeAndMsg (int code, String msg) {
  56. return new JsonData(code, null, msg);
  57. }
  58. public Integer getCode () {
  59. return code;
  60. }
  61. public void setCode (Integer code) {
  62. this.code = code;
  63. }
  64. public Object getData () {
  65. return data;
  66. }
  67. public void setData (Object data) {
  68. this.data = data;
  69. }
  70. public String getMsg () {
  71. return msg;
  72. }
  73. public void setMsg (String msg) {
  74. this.msg = msg;
  75. }
  76. }
  77. package net.wnnck.demo.model;
  78. public class VisitStatsDO {
  79. /**
  80. * 商品
  81. */
  82. private Long productId;
  83. /**
  84. * 访问时间
  85. */
  86. private String visitTime;
  87. /**
  88. * 1是新访客,0是老访客
  89. */
  90. private Integer isNew;
  91. /**
  92. * 访问量
  93. */
  94. private Integer pv;
  95. /**
  96. * 省份
  97. */
  98. private String province;
  99. /**
  100. * 城市
  101. */
  102. private String city;
  103. /**
  104. * ========度量值=========
  105. */
  106. private Long pvCount= 0L;
  107. /**
  108. * 时间的字符串映射,天、小时
  109. */
  110. private String dateTimeStr;
  111. public Long getProductId () {
  112. return productId;
  113. }
  114. public void setProductId (Long productId) {
  115. this.productId = productId;
  116. }
  117. public String getVisitTime () {
  118. return visitTime;
  119. }
  120. public void setVisitTime (String visitTime) {
  121. this.visitTime = visitTime;
  122. }
  123. public Integer getIsNew () {
  124. return isNew;
  125. }
  126. public void setIsNew (Integer isNew) {
  127. this.isNew = isNew;
  128. }
  129. public Integer getPv () {
  130. return pv;
  131. }
  132. public void setPv (Integer pv) {
  133. this.pv = pv;
  134. }
  135. public String getProvince () {
  136. return province;
  137. }
  138. public void setProvince (String province) {
  139. this.province = province;
  140. }
  141. public String getCity () {
  142. return city;
  143. }
  144. public void setCity (String city) {
  145. this.city = city;
  146. }
  147. public Long getPvCount () {
  148. return pvCount;
  149. }
  150. public void setPvCount (Long pvCount) {
  151. this.pvCount = pvCount;
  152. }
  153. public String getDateTimeStr () {
  154. return dateTimeStr;
  155. }
  156. public void setDateTimeStr (String dateTimeStr) {
  157. this.dateTimeStr = dateTimeStr;
  158. }
  159. }

 service层


  
  1. package net.wnnck.demo.service;
  2. import net.wnnck.demo.controller.request.VisitRecordPageRequest;
  3. import java.util.Map;
  4. public interface VisitStatsService {
  5. Map<String,Object> pageVisitRecord (VisitRecordPageRequest pageRequest);
  6. }
  7. package net.wnnck.demo.service.impl;
  8. import net.wnnck.demo.controller.request.VisitRecordPageRequest;
  9. import net.wnnck.demo.mapper.VisitStatsMapper;
  10. import net.wnnck.demo.model.VisitStatsDO;
  11. import net.wnnck.demo.service.VisitStatsService;
  12. import org.springframework.beans.factory.annotation.Autowired;
  13. import org.springframework.stereotype.Service;
  14. import java.util.HashMap;
  15. import java.util.List;
  16. import java.util.Map;
  17. @Service
  18. public class VisitStatsServiceImpl implements VisitStatsService {
  19. @Autowired
  20. private VisitStatsMapper visitStatsMapper;
  21. @Override
  22. public Map<String, Object> pageVisitRecord (VisitRecordPageRequest pageRequest) {
  23. Map<String,Object> data = new HashMap<>( 3);
  24. Long productId = pageRequest.getProductId();
  25. int page = pageRequest.getPage();
  26. int size = pageRequest.getSize();
  27. int count = visitStatsMapper.countTotal(productId);
  28. int from = (page - 1) * size;
  29. List<VisitStatsDO> visitStatsDOS = visitStatsMapper.pageVisitRecord(productId, from, size);
  30. data.put( "total",count);
  31. data.put( "current_page",page);
  32. data.put( "data",visitStatsDOS);
  33. /**
  34. * 计算总页数
  35. */
  36. int totalPage;
  37. if(count % size == 0){
  38. totalPage = count / size;
  39. } else {
  40. totalPage = count / size + 1 ;
  41. }
  42. data.put( "total_page",totalPage);
  43. return data;
  44. }
  45. }

启动后可正常访问,表示基础环境已搭建好。 文末有贴代码下载地址~

 五、ClickHouse+SpringBoot2.X案例-数据统计接口

 第三节的需求sql整合mapper

service层


  
  1. @Override
  2. public List<VisitStatsDO> queryVisitTrend (VisitTrendQueryRequest queryRequest) {
  3. Long productId = queryRequest.getProductId();
  4. String type = queryRequest.getType();
  5. List<VisitStatsDO> list = null;
  6. if(type.equalsIgnoreCase( "region")){
  7. list = visitStatsMapper.queryRegionTrendWithMultiDay(productId,queryRequest.getStartTime(),queryRequest.getEndTime());
  8. } else if(type.equalsIgnoreCase( "day")){
  9. list = visitStatsMapper.queryVisitTrendWithMultiDay(productId,queryRequest.getStartTime(),queryRequest.getEndTime());
  10. }
  11. return list;
  12. }

mapper层


  
  1. <select id="queryRegionTrendWithMultiDay" resultMap="BaseResultMap">
  2. select province ,city, sum(pv) pv_count from visit_stats
  3. where product_id = #{productId} and toYYYYMMDD(visit_time) BETWEEN #{startTime} and #{endTime}
  4. GROUP BY province ,city order by pv_count desc
  5. </select>
  6. <select id="queryVisitTrendWithMultiDay" resultMap="BaseResultMap">
  7. select toYYYYMMDD(visit_time) date_time_str,sum(pv) pv_count from visit_stats
  8. where product_id = #{productId} and toYYYYMMDD(visit_time) BETWEEN #{startTime} and #{endTime}
  9. GROUP BY date_time_str order by date_time_str desc
  10. </select>

 #时间范围内地区访问分布-城市级别

 多天内的访问曲线图pv

代码下载地址:

链接:https://pan.baidu.com/s/1g8dHKiZMQIhJTmuCO814hw?pwd=ex2x 
提取码:ex2x 

ClickHouse快速安装-可视化工具连接-创建第一个ck库表(一)_clickhouse可视化工具_这是王姑娘的微博的博客-CSDN博客OLAP是什么,以及快速安装ClickHouse(容器化部署),CK可视化工具的下载链接使用以及创建第一个CK数据库和表,然后新增数据,浏览3分钟即可快速掌握这些知识https://blog.csdn.net/wnn654321/article/details/125837194ClickHouse常见SQL语法和常见合并数引擎Demo(二)_这是王姑娘的微博的博客-CSDN博客分区是表的分区,把一张表的数据分成N多个区块,分区后的表还是一张表,数据处理还是由自己来完成PARTITION BY,指的是一个表按照某一列数据(比如日期)进行分区,不同分区的数据会写入不同的文件中建表时加入partition概念,可以按照对应的分区字段,允许查询在指定了分区键的条件下,尽可能的少读取数据注意:不是所有的表引擎都可以分区,合并树(MergeTree) 系列的表引擎才支持数据分区,Log系列引擎不支持。...https://blog.csdn.net/wnn654321/article/details/125920177


转载:https://blog.csdn.net/wnn654321/article/details/125952680
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场