14.【clickhouse】ClickHouse从入门到放弃-实战

简介: 【clickhouse】ClickHouse从入门到放弃-实战

前文如下:


11.【clickhouse】ClickHouse从入门到放弃-概述

12.【clickhouse】ClickHouse从入门到放弃-环境搭建

13.【clickhouse】ClickHouse从入门到放弃-引擎

4.clickhouse实战

基于上一篇的总结,做一个入门的demo,场景基于业务的spm场景:

首先先回顾一下clickhouse的优缺点。

优点:

1,为了高效的使用CPU,数据不仅仅按列存储,同时还按向量进行处理;

2,数据压缩空间大,减少IO;处理单查询高吞吐量每台服务器每秒最多数十亿行;

3,索引非B树结构,不需要满足最左原则;只要过滤条件在索引列中包含即可;即使在使用的数据不在索引中,由于各种并行处理机制ClickHouse全表扫描的速度也很快;

4,写入速度非常快,50-200M/s ,对于大量的数据更新非常适用

缺点:

1,不支持事务,不支持真正的删除/更新

2,不支持高并发,官方建议qps为100,可以通过修改配置文件增加连接数,但是在服务器足够好的情况下;

3,SQL满足日常使用80%以上的语法,join写法比较特殊;最新版已支持类似SQL的join,但性能不好

4,尽量做1000条以上批量的写入,避免逐行insert或小批量的insert,update,delete操作,因为ClickHouse底层会不断的做异步的数据合并,会影响查询性能,这个在做实时数据写入的时候要尽量避开;

5,Clickhouse快是因为采用了并行处理机制,即使一个查询,也会用服务器一半的CPU去执行,所以ClickHouse不能支持高并发的使用场景,默认单查询使用CPU核数为服务器核数的一半,安装时会自动识别服务器核数,可以通过配置文件修改该参数。

全量数据导入:数据导入临时表 -> 导入完成后,将原表改名为tmp1 -> 将临时表改名为正式表 -> 删除原表

增量数据导入:增量数据导入临时表 -> 将原数据除增量外的也导入临时表 -> 导入完成后,将原表改名为tmp1-> 将临时表改成正式表-> 删除原数据表

4.1 应用参考

基于Clickhouse的日志体系

参考文档

cloud.tencent.com/developer/a…

mp.weixin.qq.com/s/d2PbeLesL…

www.cnblogs.com/gentleschol…

4.2 clickhouse+mybatis plus集成

参考文档

https://blog.csdn.net/douglas8287/article/details/84705750
https://blog.csdn.net/xhaimail/article/details/122084999
https://www.jianshu.com/p/953ba54d434c
https://blog.csdn.net/fx9590/article/details/105163804

创建表结构

-- 本地创建SPM表:业务:用户行为分析
CREATE TABLE default.trade_spm
(
  `id` Int64,
  `user_id` Int64 COMMENT '用户id 未登录为0',
  `from_type` String COMMENT '来源:ios,
\r\nandroid,
\r\napplet',
  `client_id` String COMMENT '客户端ID',
  `spm_platfrom` String COMMENT '访问平台固定;10=ios,11=小程序,12=H5 ,
\r\n13=android',
  `spm_page` String COMMENT '页面',
  `spm_model` String COMMENT '模块',
  `spm_position` String COMMENT '位置',
  `param_content` Nullable(String) COMMENT '内容(商品ID或专题模板ID或链接)',
  `ip` String COMMENT 'IP',
  `ua` String COMMENT 'User-Agent',
  `create_time` DateTime DEFAULT toDateTime(now(), 'Asia/Shanghai') COMMENT '行为发生时间',
  `update_time` DateTime DEFAULT toDateTime(now(), 'Asia/Shanghai') COMMENT '修改时间',
  `project_code` String COMMENT '项目编码',
  `_sign` Int8 DEFAULT 1,
  `_version` UInt64 DEFAULT 1
)
ENGINE = ReplacingMergeTree(_version)
PARTITION BY intDiv(id,
18446744073709551)
ORDER BY tuple(id)
SETTINGS index_granularity = 8192

工程配置

环境dubbo+spring boot +mybatisplus +nacos+swagger ui;使用nacos作为服务注册中心和配置中心

bootstrap.yml配置文件,配置nacos配置中心,使用远程配置,配置测试环境mall-log测试环境配置文件 profiles: active为 test.

网络异常,图片无法展示
|

# nacos配置
server:
port: 8090
spring:
application:
  name: mall-log
profiles:
  active: test
cloud:
  config:
    override-none: true
    allow-override: true
    override-system-properties: false
  nacos:
    discovery:
      server-addr: 127.0.0.1:8848 #localhost:8848 #Nacos服务注册中心地址
    config:
      server-addr: 127.0.0.1:8848 #Nacos作为配置中心地址
      file-extension: yml #指定yaml格式的配置
      group: DEFAULT_GROUP
      namespace: 803931a7-6d1b-44be-a8ee-8732822722bf   #指定配置中心命名空间

登录 127.0.0.1:8848/nacos ,用户名密码nacos

nacos 点击命名空间:看到之前以及创建了一个locolhost本地测试的命名空间,id和bootstrap.yml的 namespace配置一致:

网络异常,图片无法展示
|

nacos点击配置列表,选择localhost命名空间,导入配置,或者克隆配置,文件名称和bootstrap.yml和application:name: mall-log 加上profiles:active: test一致,profiles:active区分是生产还是测试环境配置

网络异常,图片无法展示
|

编辑配置如下,文件类型选择yaml

dubbo:
application:
  name: mall-log
protocol:
  name: dubbo
  port: 20881
registry:
  address: nacos://127.0.0.1:8848   #使用本地nacos作为服务注册中心
  check: false
  timeout: 5000
scan:
  base-packages: com.anchu.log.web.service
config-center:
  check:
consumer:
  check: false
  timeout: 5000
server:
port: 8090
spring:
cloud:
  config:
    override-none: true
datasource:
  driver-class-name: com.clickhouse.jdbc.ClickHouseDriver
  url: jdbc:clickhouse://192.168.120.110:8123/default
  userName: default
  password: clickhouse
  druid:
     # 按照自己连接的 clickhouse 数据库来
    filters: none  #clickhouse不支持wall及监控
     #配置初始化大小/最小/最大
    initial-size: 5
    min-idle: 5
    max-active: 20
     #获取连接等待超时时间
    max-wait: 60000
     #间隔多久进行一次检测,检测需要关闭的空闲连接
    time-between-eviction-runs-millis: 60000
     #一个连接在池中最小生存的时间
    min-evictable-idle-time-millis: 30000
    validation-query: SELECT 1
#mybatis
mybatis-plus:
mapper-locations: classpath:mapper/*.xml
configuration:
  log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
  db-config:
    logic-delete-field: flag  # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
    logic-delete-value: 1 # 逻辑已删除值(默认为 1)
    logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
logging:
config: classpath:log4j2.xml

代码编写(部分逻辑,其实实现和操作关系型数据库类似)

启动类,@EnableDiscoveryClient注解,服务发现

/**
* @author hh
* @date 2022/4/6 16:26
*/
@SpringBootApplication(scanBasePackages = {"com.anchu.log"})
@MapperScan(basePackages = {"com.anchu.log.dao"})
@EnableConfigurationProperties
@EnableDiscoveryClient
@EnableAsync
public class WsnbLogApplication {
   public static void main(String[] args) {
       SpringApplication.run(WsnbLogApplication.class, args);
  }
}

伪代码如下:

//controller
@RestController
@RequestMapping("/spm")
@Api(tags = "用户端SPM埋点")
public class TradeSpmAppController {
   @Reference
   private JwtService jwtService;
   @Autowired
   private ITradeSpmService iTradeSpmService;
   @ApiOperation(value = "增加埋点点击事件", httpMethod = "POST")
   @PostMapping("add")
   public ResultEntity add(HttpServletRequest request, @RequestBody TradeSpmDto dto){
       if(Objects.isNull(dto)){
           throw new CustomException(ResultEnum.PARAMS_IS_EMPTY);
      }
       String token = request.getHeader(ShiroConstants.AUTHORIZATION);
       String userAgent = request.getHeader("User-Agent");
//       if(!Objects.isNull(token) && !("".equals(token))){
//           TradeUserDto userDto =jwtService.parseToken(token);
//           dto.setUserId(userDto.getUserId());
//       }
       dto.setUserId(1L);
       dto.setIp(IPUtil.getIp(request));
       dto.setUa(Objects.isNull(userAgent) ? "unknow" : userAgent);
       iTradeSpmService.add(dto);
       return ResultTemplate.success();
  }
}
//service 
public interface ITradeSpmService {
    void add(TradeSpmDto dto);
}
@Service
public class TradeSpmLogServiceImpl extends ServiceImpl<TradeSpmLogMapper,
TradeSpmLog> implements ITradeSpmLogService {
    @Override
    public void add(TradeSpmLogDto dto) {
        if(Objects.isNull(dto)){
            throw new CustomException(ResultEnum.PARAMS_IS_EMPTY);
        }
        TradeSpmLog entity = dto.cloneToDomain(TradeSpmLog.class);
        //clickhouse 不支持自增主键,雪花算法生成
        entity.setId(SnowflakeIdWorker.generateId());
        this.baseMapper.insert(entity);
    }
}
//Mapper
public interface TradeSpmLogMapper extends BaseMapper<TradeSpmLog> {
}
//dao,dto,entity省略
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
@Data
@EqualsAndHashCode(callSuper = false)
public class TradeSpm extends BaseObject {
    private static final long serialVersionUID = 1L;
    //@TableId(value = "id", type = IdType.AUTO) 不支持自增
    private Long id;
    private Long userId;
    /**
     * 来源:ios,android,applet
     */
    private String fromType;
    /**
     * 客户端ID
     */
    private String clientId;
    /**
     * 访问平台固定;10=APP,11=小程序,12=H5
     */
    private String spmPlatfrom;
    /**
     * 页面
     */
    private String spmPage;
    /**
     * 模块
     */
    private String spmModel;
    /**
     * 位置
     */
    private String spmPosition;
    /**
     * 内容
     */
    private String paramContent;
    /**
     * IP
     */
    private String ip;
    /**
     * User-Agent
     */
    private String ua;
    /**
     * 创建时间
     */
    private Date createTime;
    /**
     * 修改时间
     */
    private Date updateTime;
}

swagger ui测试:

http://127.0.0.1:8090/doc.html

网络异常,图片无法展示
|

//请求体
{
"clientId": "1",
"fromType": "1",
"id": 0,
"ip": "127.0.0.1",
"paramContent": "{"goodId":1}",
"spmModel": "10",
"spmPage": "11",
"spmPlatfrom": "11",
"spmPosition": "111",
"ua": "microsoft edge",
"userId": 1
}

问题

(1)主键不支持自增,可以业务代码自己生成,比如雪花算法id

(2)时区问题

blog.csdn.net/tomMMMMMMMM…

# 确保机器时间正确 时区确保正确,PDT是太平洋夏季时间。
[root@localhost anchu]# date -s 2022-05-18
Wed May 18 00:00:00 PDT 2022
[root@localhost anchu]# date -s 11:14:20
Wed May 18 11:14:20 PDT 2022
[root@localhost anchu]# hwclock --systohc
[root@localhost anchu]# date
Wed May 18 11:14:48 PDT 2022
[root@localhost anchu]#
#修改时区
[root@localhost anchu]# ll /etc/localtime
lrwxrwxrwx. 1 root root 41 Apr 22 12:36 /etc/localtime -> ../usr/share/zoneinfo/America/Los_Angeles
[root@localhost anchu]# ll /etc/localtime
lrwxrwxrwx. 1 root root 41 Apr 22 12:36 /etc/localtime -> ../usr/share/zoneinfo/America/Los_Angeles
[root@localhost anchu]# ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
[root@localhost anchu]# ll /etc/localtime
lrwxrwxrwx. 1 root root 33 May 19 03:39 /etc/localtime -> /usr/share/zoneinfo/Asia/Shanghai
[root@localhost anchu]# date
Thu May 19 03:39:22 CST 2022
[root@localhost anchu]# date -s 2022-05-18
Wed May 18 00:00:00 CST 2022
[root@localhost anchu]# date -s 12:48:30
Wed May 18 12:48:30 CST 2022
#修改clickhouse配置文件,并重启
[root@localhost anchu]# vi /etc/clickhouse-server/config.xml
<timezone>Asia/Shanghai</timezone>
[root@localhost anchu]# clickhouse restart
#查看clickhouse时区配置
localhost :) show SETTINGS like '%time_zone%';
SHOW SETTINGS LIKE '%time_zone%'
Query id: a31192e8-b011-4117-91ce-2abc86bbbeef
┌─name─────────────────┬─type─┬─value─┐
│ use_client_time_zone │ Bool │ 0     │
└──────────────────────┴──────┴───────┘
localhost :) show SETTINGS like '%time_zone%';
SET use_client_time_zone = 1;
localhost :) select toTimeZone(toDateTime(now()), 'Asia/Shanghai');
┌─toTimeZone(toDateTime(now()), 'Asia/Shanghai')─┐
│                            2022-05-18 12:50:06 │
└────────────────────────────────────────────────┘

(3)性能问题

由于clickhouse对高并发支持不高,其次类似标题5.1说明,避免逐行insert或小批量的insert,update,delete操作,尽量做1000条以上批量的写入,我们可以优化代码,先将spm数据队列缓存起来,后面多并发批处理。

队列及处理相关参考文档:

blog.csdn.net/qq_34561243…


相关文章
|
存储 数据库 索引
61.【clickhouse】ClickHouse从入门到放弃-MergeTree的存储结构
【clickhouse】ClickHouse从入门到放弃-MergeTree的存储结构
61.【clickhouse】ClickHouse从入门到放弃-MergeTree的存储结构
|
OLAP 数据库 索引
59.【clickhouse】ClickHouse从入门到放弃-分区表
【clickhouse】ClickHouse从入门到放弃-分区表
59.【clickhouse】ClickHouse从入门到放弃-分区表
|
存储 索引
67.【clickhouse】ClickHouse从入门到放弃-对于分区、索引、标记和压缩数据的协同总结
【clickhouse】ClickHouse从入门到放弃-对于分区、索引、标记和压缩数据的协同总结
67.【clickhouse】ClickHouse从入门到放弃-对于分区、索引、标记和压缩数据的协同总结
|
存储 搜索推荐 关系型数据库
55.【clickhouse】ClickHouse从入门到放弃-概念场景
【clickhouse】ClickHouse从入门到放弃-概念场景
55.【clickhouse】ClickHouse从入门到放弃-概念场景
|
SQL 存储 缓存
13.【clickhouse】ClickHouse从入门到放弃-引擎
【clickhouse】ClickHouse从入门到放弃-引擎
13.【clickhouse】ClickHouse从入门到放弃-引擎
|
SQL 存储 数据库
12.【clickhouse】ClickHouse从入门到放弃-环境搭建
【clickhouse】ClickHouse从入门到放弃-环境搭建
12.【clickhouse】ClickHouse从入门到放弃-环境搭建
|
存储 缓存 数据库
66.【clickhouse】ClickHouse从入门到放弃-数据标记
【clickhouse】ClickHouse从入门到放弃-数据标记
66.【clickhouse】ClickHouse从入门到放弃-数据标记
|
存储 算法 NoSQL
65.【clickhouse】ClickHouse从入门到放弃-数据存储
【clickhouse】ClickHouse从入门到放弃-数据存储
65.【clickhouse】ClickHouse从入门到放弃-数据存储
|
存储 数据库 索引
64.【clickhouse】ClickHouse从入门到放弃-二级索引
【clickhouse】ClickHouse从入门到放弃-二级索引
64.【clickhouse】ClickHouse从入门到放弃-二级索引
|
存储 算法 数据库
63.【clickhouse】ClickHouse从入门到放弃-一级索引
【clickhouse】ClickHouse从入门到放弃-一级索引
63.【clickhouse】ClickHouse从入门到放弃-一级索引

热门文章

最新文章