数仓实战 - 滴滴出行

news2024/10/5 21:14:20

项目大致流程:
在这里插入图片描述

1、项目业务背景

1.1 目的

本案例将某出行打车的日志数据来进行数据分析,例如:我们需要统计某一天订单量是多少、预约订单与非预约订单的占比是多少、不同时段订单占比等
数据海量 – 大数据
hive比MySQL慢很多

1.2 项目架构

  1. 用户打车的订单数据非常庞大。所以我们需要选择一个大规模数据的分布式文件系统来存储这些日志文件,此处,我们基于Hadoop的HDFS文件系统来存储数据。
  2. 为了方便进行数据分析,我们要将这些日志文件的数据映射为一张一张的表,所以,我们基于Hive来构建数据仓库。所有的数据,都会在Hive下来几种进行管理。为了提高数据处理的性能。
  3. 我们将基于MR引擎来进行数据开发。
  4. 我们将使用Zeppelin来快速将数据进行SQL指令交互。
  5. 我们使用Sqoop导出分析后的数据到传统型数据库,便于后期应用
  6. 我们使用fineBI来实现数据可视化展示
    在这里插入图片描述

2、日志数据集介绍

四张表:打车表,取消订单表,支付表,评价表

1,日志数据文件
处理的数据都是一些文本日志,例如:以下就是一部门用户打车的日志文件。
在这里插入图片描述
一行就是一条打车订单数据,而且,一条数据是以逗号来进行分隔的,逗号分隔出来一个个的字段。
在这里插入图片描述
2,用户取消订单日志
当用户取消订单时,也会在系统后台产生一条日志。用户需求选择取消订单的原因。
在这里插入图片描述
3,用户支付日志
用户点击确认支付后,系统后台会将用户的支持信息保存为一条日志。
在这里插入图片描述
4,用户评价日志
我们点击提交评价后,系统后台也会产生一条日志。

在这里插入图片描述

3、数据仓库构建

面试问题:数仓如何从0到1?

我们的目标是分析用户打车的订单,进行各类的指标计算(指标,例如:订单的总数、订单的总支付金额等等)。

思想:可以将日志数据上传到HDFS保存下来,每天都可以进行上传,HDFS可以保存海量的数据。同时,可以将HDFS中的数据文件,对应到Hive的表中。但需要考虑一个问题,就是业务系统的日志数据不一定是能够直接进行分析的,

例如:我们需要分析不同时段的订单占比,凌晨有多少订单、早上有多少订单、上午有多少订单等。但是,我们发现,原始的日志文件中,并没有区分该订单的是哪个时间段的字段。所以,我们需要对日志文件的原始数据进行预处理,才能进行分析。

我们会有这么几类数据要考虑:

  1. 原始日志数据(业务系统中保存的日志文件数据) ods
  2. 预处理后的数据 dw
  3. 分析结果数据 app

这些数据我们都通过Hive来进行处理,因为Hive可以将数据映射为一张张的表,然后就可以通过编写HQL来处理数据了,简单、快捷、高效。为了区分以上这些数据,我们将这些数据对应的表分别保存在不同的数据库中。

为了方便组织、管理上述的三类数据,我们将数仓分成不同的层,简单来说,就是分别将三类不同的数据保存在Hive的不同数据库中。
在这里插入图片描述
1)在hive构建三层数据仓库:ods、dw、app:

--1:创建数据库 
    -- 1.1 创建ods库
    create database if not exists ods_didi;
    -- 1.2 创建dw库
    create database if not exists dw_didi;
    -- 1.3 创建app库
    create database if not exists app_didi;

2)在ods层创建四张表

--2:创建表
-- 2.1 创建订单表结构
    create table if not exists ods_didi.t_user_order(
        orderId string comment '订单id',
        telephone string comment '打车用户手机',
        lng string comment '用户发起打车的经度',
        lat string comment '用户发起打车的纬度',
        province string comment '所在省份',
        city string comment '所在城市',
        es_money double comment '预估打车费用',
        gender string comment '用户信息 - 性别',
        profession string comment '用户信息 - 行业',
        age_range string comment '年龄段(70后、80后、...)',
        tip double comment '小费',
        subscribe int comment '是否预约(0 - 非预约、1 - 预约)',
        sub_time string comment '预约时间',
        is_agent int comment '是否代叫(0 - 本人、1 - 代叫)',
        agent_telephone string comment '预约人手机',
        order_time string comment '预约时间'
    )
    partitioned by (dt string comment '时间分区') 
    ROW FORMAT DELIMITED FIELDS TERMINATED BY ',' ; 
--2.2 创建取消订单表
create table if not exists ods_didi.t_user_cancel_order(
    orderId string comment '订单ID',
    cstm_telephone string comment '客户联系电话',
    lng string comment '取消订单的经度',
    lat string comment '取消订单的纬度',
    province string comment '所在省份',
    city string comment '所在城市',
    es_distance double comment '预估距离',
    gender string comment '性别',
    profession string comment '行业',
    age_range string comment '年龄段',
    reason int comment '取消订单原因(1 - 选择了其他交通方式、2 - 与司机达成一致,取消订单、3 - 投诉司机没来接我、4 - 已不需要用车、5 - 无理由取消订单)',
    cancel_time string comment '取消时间'
)
partitioned by (dt string comment '时间分区') 
ROW FORMAT DELIMITED FIELDS TERMINATED BY ',' ; 
--2.3 创建订单支付表
create table if not exists ods_didi.t_user_pay_order(
    id string comment '支付订单ID',
    orderId string comment '订单ID',
    lng string comment '目的地的经度(支付地址)',
    lat string comment '目的地的纬度(支付地址)',
    province string comment '省份',
    city string comment '城市',
    total_money double comment '车费总价',
    real_pay_money double comment '实际支付总额',
    passenger_additional_money double comment '乘客额外加价',
    base_money double comment '车费合计',
    has_coupon int comment '是否使用优惠券(0 - 不使用、1 - 使用)',
    coupon_total double comment '优惠券合计',
    pay_way int comment '支付方式(0 - 微信支付、1 - 支付宝支付、3 - QQ钱包支付、4 - 一网通银行卡支付)',
    mileage double comment '里程(单位公里)',
    pay_time string comment '支付时间'
)
partitioned by (dt string comment '时间分区') 
ROW FORMAT DELIMITED FIELDS TERMINATED BY ',' ; 

--2.4创建用户评价表
create table if not exists ods_didi.t_user_evaluate(
    id string comment '评价日志唯一ID',
    orderId string comment '订单ID',
    passenger_telephone string comment '用户电话',
    passenger_province string comment '用户所在省份',
    passenger_city string comment '用户所在城市',
    eva_level int comment '评价等级(1 - 一颗星、... 5 - 五星)',
    eva_time string comment '评价时间'
)
partitioned by (dt string comment '时间分区') 
ROW FORMAT DELIMITED FIELDS TERMINATED BY ',' ; 

comment—注释

show tables  --查看一下

4、数据分区表构建

3)表数据加载
大规模数据的处理,必须要构建分区。我们此处的需求每天都会进行数据分析,采用的是T+1的模式。

就是假设今天是2021-01-01,那么1月1日的分析结果在第二天才能看到,也就是2021-01-02查看到上一天的数据分析结果。此处,我们采用最常用的分区方式,使用日期来进行分区。

-3:给表加载数据
--3.1、创建本地路径,上传源日志文件
mkdir -p /export/data/didi

--3.2、通过load命令给表加载数据,并指定分区
load data local inpath '/export/data/didi/order.csv' into table t_user_order partition (dt='2020-04-12');
load data local inpath '/export/data/didi/cancel_order.csv' into table t_user_cancel_order partition (dt='2020-04-12');
load data local inpath '/export/data/didi/pay.csv' into table t_user_pay_order partition (dt='2020-04-12');
load data local inpath '/export/data/didi/evaluate.csv' into table t_user_evaluate partition (dt='2020-04-12');

加载完数据后也是查看一下,数据是否成功进入;

select * from t_user_order limit 1;
select * from t_user_cancel_order limit 1;
select * from t_user_pay_order limit 1;
select * from t_user_evaluate limit 1;

5、数据预处理

现在数据已经准备好了,接下来需要对ods层中的数据进行预处理。

数据预处理是数据仓库开发中的一个重要环节。目的主要是让预处理后的数据更容易进行数据分析,并且能够将一些非法的数据处理掉,避免影响实际的统计结果。

需要在预处理之前考虑以下需求:

  1. 过滤掉order_time长度小于8的数据,如果小于8,表示这条数据不合法,不应该参加统计。—length(order_time)>=8
  2. 将一些0、1表示的字段,处理为更容易理解的字段。例如:subscribe字段,0表示非预约、1表示预约。我们需要添加一个额外的字段,用来展示非预约和预约,这样将来我们分析的时候,跟容易看懂数据。—case when
  3. order_time字段为2020-4-12 1:15,为了将来更方便处理,我们统一使用类似 2020-04-12 01:15:00来表示,这样所有的order_time字段长度是一样的。并且将日期获取出来 为了方便将来按照年、月、日、小时统计,我们需要新增这几个字段。后续要分析一天内,不同时段的订单量,我们需要在预处理过程中将订单对应的时间段提前计算出来。例如:1:00-5:00为凌晨。

在这里插入图片描述
多增加了7个字段—形成宽表,包含以下字段:
在这里插入图片描述

--4:数据预处理
--建表
create table if not exists dw_didi.t_user_order_wide(
    orderId string comment '订单id',
    telephone string comment '打车用户手机',
    lng string comment '用户发起打车的经度',
    lat string comment '用户发起打车的纬度',
    province string comment '所在省份',
    city string comment '所在城市',
    es_money double comment '预估打车费用',
    gender string comment '用户信息 - 性别',
    profession string comment '用户信息 - 行业',
    age_range string comment '年龄段(70后、80后、...)',
    tip double comment '小费',
    subscribe int comment '是否预约(0 - 非预约、1 - 预约)',
    subscribe_name string comment '是否预约名称',
    sub_time string comment '预约时间',
    is_agent int comment '是否代叫(0 - 本人、1 - 代叫)',
    is_agent_name string comment '是否代叫名称',
    agent_telephone string comment '预约人手机',
    order_date string comment '预约时间,yyyy-MM-dd',
    order_year string comment '年',
    order_month string comment '月',
    order_day string comment '日',
    order_hour string comment '小时',
    order_time_range string comment '时间段',
    order_time string comment '预约时间'
)
partitioned by (dt string comment '时间分区') 
ROW FORMAT DELIMITED FIELDS TERMINATED BY ',' ; 

--转宽表HQL语句
--------------------------------------
--date_format将字符串转为日期
select date_format('2020-1-1', 'yyyy-MM-dd'); -- 2020-01-01
select date_format('2020-1-1 12:23', 'yyyy-MM-dd'); -- 2020-01-01
select date_format('2020-1-1 12:23:35', 'yyyy-MM-dd'); -- 2020-01-01

select date_format('2020-1-1 1:1:1', 'yyyy-MM-dd HH:mm:ss'); -- 2020-01-01 01:01:01

select hour(date_format('2020-1-1 1:1:00', 'yyyy-MM-dd HH:mm:ss')); -- 2020-01-01 01:01:01


--concat字符串的拼接
select concat('aaa','bbb','ccc');-- aaabbbccc
--length 获取字符串长度
select length('aaabbb'); -- 6

一天新增4500TB,一个服务器的磁盘存储容量10T

生成宽表后,往宽表中插入数据

如何将一个表的查询结果保存到另外一张表:
insert overwrite table 表名1 select 字段 from 表名2

insert overwrite table dw_didi.t_user_order_wide partition(dt='2020-04-12')
select 
    orderId,
    telephone,
    lng,
    lat,
    province,
    city,
    es_money,
    gender,
    profession,
    age_range,
    tip,
    subscribe,
    case when subscribe = 0 then '非预约'
         when subscribe = 1 then'预约'
    end as subscribe_name,
     date_format(concat(sub_time,':00'), 'yyyy-MM-dd HH:mm:ss') as sub_time,
    is_agent,
    case when is_agent = 0 then '本人'
         when is_agent = 1 then '代叫'
    end as is_agent_name,
    agent_telephone,
    date_format(order_time, 'yyyy-MM-dd') as order_date, -- 2020-1-1 --->2020-01-01
    year(date_format(order_time, 'yyyy-MM-dd')) as order_year, --2020
    month(date_format(order_time, 'yyyy-MM-dd')) as order_month, --12
    day(date_format(order_time, 'yyyy-MM-dd')) as order_day, --23
    hour(date_format(concat(order_time,':00'), 'yyyy-MM-dd HH:mm:ss')) as order_hour,
    case when hour(date_format(concat(order_time,':00'), 'yyyy-MM-dd HH:mm:ss')) > 1 and hour(date_format(concat(order_time,':00'), 'yyyy-MM-dd HH:mm:ss')) <= 5 then '凌晨'
         when hour(date_format(concat(order_time,':00'), 'yyyy-MM-dd HH:mm:ss')) > 5 and hour(date_format(concat(order_time,':00'), 'yyyy-MM-dd HH:mm:ss')) <= 8 then '早上'
         when hour(date_format(concat(order_time,':00'), 'yyyy-MM-dd HH:mm:ss')) > 8 and hour(date_format(concat(order_time,':00'), 'yyyy-MM-dd HH:mm:ss')) <= 11 then '上午'
         when hour(date_format(concat(order_time,':00'), 'yyyy-MM-dd HH:mm:ss')) > 11 and hour(date_format(concat(order_time,':00'), 'yyyy-MM-dd HH:mm:ss')) <= 13 then '中午'
         when hour(date_format(concat(order_time,':00'), 'yyyy-MM-dd HH:mm:ss')) > 13 and hour(date_format(concat(order_time,':00'), 'yyyy-MM-dd HH:mm:ss')) <= 17 then '下午'
         when hour(date_format(concat(order_time,':00'), 'yyyy-MM-dd HH:mm:ss')) > 17 and hour(date_format(concat(order_time,':00'), 'yyyy-MM-dd HH:mm:ss')) <= 19 then '晚上'
         when hour(date_format(concat(order_time,':00'), 'yyyy-MM-dd HH:mm:ss')) > 19 and hour(date_format(concat(order_time,':00'), 'yyyy-MM-dd HH:mm:ss')) <= 20 then '半夜'
         when hour(date_format(concat(order_time,':00'), 'yyyy-MM-dd HH:mm:ss')) > 20 and hour(date_format(concat(order_time,':00'), 'yyyy-MM-dd HH:mm:ss')) <= 24 then '深夜'
         when hour(date_format(concat(order_time,':00'), 'yyyy-MM-dd HH:mm:ss')) >= 0 and hour(date_format(concat(order_time,':00'), 'yyyy-MM-dd HH:mm:ss')) <= 1 then '深夜'
         else 'N/A'
    end as order_time_range,
    date_format(concat(order_time,':00'), 'yyyy-MM-dd HH:mm:ss') as order_time
from ods_didi.t_user_order where dt = '2020-04-12' and length(order_time) >= 8;

6、订单指标分析

1 总订单笔数

select 
    count(orderid) as total_cnt
from
    dw_didi.t_user_order_wide
where
    dt = '2020-04-12'
;
-- 创建保存日期对应订单笔数的app表
create table if not exists app_didi.t_order_total(
    date_val string comment '日期(年月日)',
    count int comment '订单笔数'
)
partitioned by (month string comment '年月,yyyy-MM')
row format delimited fields terminated by ','
;

--加载数据到app表
insert overwrite table app_didi.t_order_total partition(month='2020-04')
select 
    '2020-04-12',
    count(orderid) as total_cnt
from
    dw_didi.t_user_order_wide
where
    dt = '2020-04-12'
;
  1. 预约和非预约用户占比
    需求:
    求出预约用户订单所占的百分比:

select 预约订单总数 / 总订单数 from 预约统计订单数表,总订单数表
union all
select 非预约订单总数 / 总订单数 from 非预约统计订单数表,总订单数表

select 
   '2020-04-12',
    '预约',
    concat(round(t1.total_cnt /t2.total_cnt *100,2),'%') as subscribe 
from 
   (
      select 
            count(orderid) as total_cnt
        from
            dw_didi.t_user_order_wide
        where
            subscribe = 1 and dt = '2020-04-12' 
    )t1,
    (
        select 
            count(orderid) as total_cnt
        from
            dw_didi.t_user_order_wide
        where
            dt = '2020-04-12'
    )t2

union all --将上边的查询结果和下边的查询结果进行合并

select 
   '2020-04-12',
    '非预约',
    concat(round(t1.total_cnt /t2.total_cnt *100,2),'%') as nosubscribe 
from 
   (
      select 
            count(orderid) as total_cnt
        from
            dw_didi.t_user_order_wide
        where
            subscribe = 0 and dt = '2020-04-12' 
    )t1,
    (
        select 
            count(orderid) as total_cnt
        from
            dw_didi.t_user_order_wide
        where
            dt = '2020-04-12'
    )t2

-- 创建保存日期对应订单笔数的app表
create table if not exists app_didi.t_order_subscribe_percent(
    date_val string comment '日期',
    subscribe_name string comment '是否预约',
    percent_val string comment '百分比'
)partitioned by (month string comment '年月yyyy-MM') 
row format delimited fields terminated by ','

--加载数据到app表
insert overwrite table  app_didi.t_order_subscribe_percent partition(month='2020-04')
select 
   '2020-04-12',
    '预约',
    concat(round(t1.total_cnt /t2.total_cnt *100,2),'%') as subscribe 
from 
   (
      select 
            count(orderid) as total_cnt
        from
            dw_didi.t_user_order_wide
        where
            subscribe = 1 and dt = '2020-04-12' 
    )t1,
    (
        select 
            count(orderid) as total_cnt
        from
            dw_didi.t_user_order_wide
        where
            dt = '2020-04-12'
    )t2

union all
select 
   '2020-04-12',
    '非预约',
    concat(round(t1.total_cnt /t2.total_cnt *100,2),'%') as nosubscribe 
from 
   (
      select 
            count(orderid) as total_cnt
        from
            dw_didi.t_user_order_wide
        where
            subscribe = 0 and dt = '2020-04-12' 
    )t1,
    (
        select 
            count(orderid) as total_cnt
        from
            dw_didi.t_user_order_wide
        where
            dt = '2020-04-12'
    )t2

3 不同时段的占比分析

--编写HQL语句
select
    order_time_range,
    count(*) as order_cnt
from
    dw_didi.t_user_order_wide
where
    dt = '2020-04-12'
group by
    order_time_range
--创建APP层表
create table if not exists app_didi.t_order_timerange_total(
    date_val string comment '日期',
    timerange string comment '时间段',
    count int comment '订单数量'
)
partitioned by (month string comment '年月,yyyy-MM')
row format delimited fields terminated by ','
;

--加载数据到APP表
insert overwrite table app_didi.t_order_timerange_total partition(month = '2020-04')
select
    '2020-04-12',
    order_time_range,
    count(*) as order_cnt
from
    dw_didi.t_user_order_wide
where
    dt = '2020-04-12'
group by
    order_time_range
;

4 不同地域订单占比

--编写HQL ---方式1
select
    province,
    count(*) as order_cnt
from
    dw_didi.t_user_order_wide
where
    dt = '2020-04-12'
group by
    province
;

--编写HQL ---方式2
select * from
(
	select 
	*,
	dense_rank() over(partition by province order by t.total_cnt desc) as rk
	from
	(
		select 
		 '2020-04-12',
		  province,
		  city,
		  count(orderid) as total_cnt
		from dw_didi.t_user_order_wide
		group by province,city
	)t
)tt
where tt.rk <=3;


--创建APP表
create table if not exists app_didi.t_order_province_total(
    date_val string comment '日期',
    province string comment '省份',
    count int comment '订单数量'
)
partitioned by (month string comment '年月,yyyy-MM')
row format delimited fields terminated by ','
;

--数据加载到APP表
insert overwrite table app_didi.t_order_province_total partition(month = '2020-04')
select
    '2020-04-12',
    province,
    count(*) as order_cnt
from
    dw_didi.t_user_order_wide
where
    dt = '2020-04-12'
group by
    province
order by order_cnt desc
;

5 不同年龄段,不同时段订单占比

--不同年龄段的订单统计
select 
'2020-04-12',
 age_range,
 count(*)
from dw_didi.t_user_order_wide
where dt='2020-04-12'
group by age_range

--不同时段的订单统计
select 
'2020-04-12',
 order_time_range,
 count(*)
from dw_didi.t_user_order_wide
where dt='2020-04-12'
group by order_time_range


--不同年龄段,不同时段的订单统计
select
   '2020-04-12',
    age_range,
    order_time_range,
    count(*) as order_cnt
from
    dw_didi.t_user_order_wide
where
    dt = '2020-04-12'
group by
    age_range,
    order_time_range
;

--创建APP表
create table if not exists app_didi.t_order_age_and_time_range_total(
    date_val string comment '日期',
    age_range string comment '年龄段',
    order_time_range string comment '时段',
    count int comment '订单数量'
)
partitioned by (month string comment '年月,yyyy-MM')
row format delimited fields terminated by ','
;

--加载数据到APP表
insert overwrite table app_didi.t_order_age_and_time_range_total partition(month = '2020-04')
select
      '2020-04-12',
      age_range,
     order_time_range,
    count(*) as order_cnt
from
    dw_didi.t_user_order_wide
where
    dt = '2020-04-12'
group by
    age_range,
    order_time_range
;

7、Sqoop数据导出

Sqoop安装 
-- 准备工作

#验证sqoop是否工作
/export/server/sqoop-1.4.7/bin/sqoop list-databases \
--connect jdbc:mysql://192.168.88.100:3306/ \
--username root \
--password 123456 

--1:mysql创建目标数据库和目标表
    #创建目标数据库
    create database if not exists app_didi;
     
    #创建订单总笔数目标表
    create table if not exists app_didi.t_order_total(
        order_date date,
        count int
    );

 --2:导出订单总笔数表数据
    /export/server/sqoop-1.4.7/bin/sqoop export \
    --connect jdbc:mysql://192.168.88.100:3306/app_didi \
    --username root \
    --password 123456 \
    --table t_order_total \
    --export-dir /user/hive/warehouse/app_didi.db/t_order_total/month=2020-04

8、finebi数据可视化

--Superset可视化

superset run -h 192.168.88.100 -p 8099 --with-threads --reload --debugger

mysql+pymysql://root:123456@192.168.88.100/app_didi?charset=utf8

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

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

相关文章

【K6】使用InfluxDB和Grafana图像化展示k6.io的测试数据

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录前言一&#xff1a;什么是K6二&#xff1a;K6的安装三&#xff1a;脚本准备四&#xff1a;执行脚本五&#xff1a;结果分析六、输出结果总结前言 ●&#x1f9d1;个人…

(考研湖科大教书匠计算机网络)第四章网络层-第六节4:边界网关协议BGP的基本工作原理

获取pdf&#xff1a;密码7281专栏目录首页&#xff1a;【专栏必读】考研湖科大教书匠计算机网络笔记导航 文章目录一&#xff1a;不同自治系统交流需要考虑的问题二&#xff1a;边界网关协议BGP概述三&#xff1a;BGP-4种的四种报文本节对应视频如下 【计算机网络微课堂&#x…

告警修复难?Zabbix+ChatGPT,轻松化解大胆尝试!

感谢本文作者小谈谈&#xff01; ○ 基于 Zabbix 能力&#xff0c;我们将告警发给了 ChatGPT&#xff0c;并通过企业微信内部应用的方式给出告警信息和修复建议。效果如下图&#xff1a; 摘 要 ChatGPT 是最近很火的 AI 智能机器人程序&#xff0c;2 个月活跃用户突破 1 亿&a…

java对象内存结构分析与大小计算

java对象内存结构Java对象保存在堆中时&#xff0c;由三部分组成&#xff1a;对象头&#xff08;object header&#xff09;&#xff1a;包括了关于堆对象的布局、类型、GC状态、同步状态和标识哈希码的基本信息。所有java对象都有一个共同的对象头格实例数据&#xff08;Insta…

55个软件测试工具,正在做测试的你get到了吗

网络“黑色星期五”威胁&#xff0c;安全漏洞&#xff0c;网上银行盗窃&#xff0c;系统停机时间&#xff0c;以及许多这样的恶梦让全球的企业忧心忡忡难以入眠。确保性能具有加载的安全性和增强的经验是这个领域每个有能力的玩家所必备的。 我们为你提供了一个丰富的软件测试…

Word控件Spire.Doc 【Table】教程(15):如何在 C# 中对齐表格

Spire.Doc for .NET是一款专门对 Word 文档进行操作的 .NET 类库。在于帮助开发人员无需安装 Microsoft Word情况下&#xff0c;轻松快捷高效地创建、编辑、转换和打印 Microsoft Word 文档。拥有近10年专业开发经验Spire系列办公文档开发工具&#xff0c;专注于创建、编辑、转…

Java字符流

5 字符流 5.1 为什么出现字符流 由于字节流操作中文不是特别的方便&#xff0c;所以Java就提供字符流字符流字节流编码表用字节流复制文本文件时&#xff0c;文本文件也会有中文&#xff0c;但是没有问题&#xff0c;原因是最终底层操作会自动进行字节拼接成中文&#xff0c;…

【面试题】对JS中的事件冒泡、事件捕获、事件委托的理解

大厂面试题分享 面试题库后端面试题库 &#xff08;面试必备&#xff09; 推荐&#xff1a;★★★★★地址&#xff1a;前端面试题库DOM事件流&#xff08;event flow &#xff09;存在三个阶段&#xff1a;事件捕获阶段、处于目标阶段、事件冒泡阶段。Dom标准事件流的触发的先…

ChatGPT的原理:机器人背后的模型

ChatGPT的原理&#xff1a;机器人背后的模型 翻译&#xff1a;老齐 本文将深入讲解支持 ChatGPT 的机器学习模型。从介绍大语言模型&#xff08;Large Language Models&#xff09;开始&#xff0c;深入探讨革命性的自注意力机制&#xff0c;该机制使 GPT-3 得以被训练。然后&a…

数据库查询计划:获取与分类

数据库在得到一个查询后&#xff0c;先将查询转化为一个逻辑查询计划&#xff0c;对其进行优化&#xff0c;然后转为物理执行计划&#xff0c;最后按照物理执行计划进行操作&#xff0c;最终得到最终结果。本篇博客介绍获得数据库查询计划的语法与物理计划的分类。 目录得到查询…

文件描述符fd

目录文件描述符fd系统文件IO接口介绍openclosewriteopen的函数返回值0 & 1 & 2文件描述符的分配规则重定向dup2系统调用FILE文件描述符fd 操作系统是文件的管理者&#xff0c;所有语言上的对“文件”的操作&#xff0c;都必须贯穿OS&#xff0c;又因为操作系统不相信任…

A_A07_003 AS608指纹模块上位机软件使用

A_A07_003 AS608指纹模块上位机软件使用一、软件获取二、测试用模块与接线三、上位机界面分块和功能1、指纹图像显示区域2、硬件信息3、打开设备4、设备配置5、信息显示6、图像管理7、指纹处理8、辅助功能四、注意事项一、软件获取 网盘链接 直戳跳转 二、测试用模块与接线 …

【Kubernetes】【五】资源管理和YML

第三章 资源管理 本章节主要介绍yaml语法和kubernetes的资源管理方式 资源管理介绍 在kubernetes中&#xff0c;所有的内容都抽象为资源&#xff0c;用户需要通过操作资源来管理kubernetes。 ​ kubernetes的本质上就是一个集群系统&#xff0c;用户可以在集群中部署各种服务…

软件测试面试刷题app包含了各种难题

软件测试的生命周期&#xff1a; V模型&#xff1a;与软件开发阶段呼应 软件开发&#xff1a;需求分析-->概要设计-->详细设计-->编码阶段软件测试&#xff1a;单元测试-->集成测试-->系统测试-->验收测试从基本流程的角度讲&#xff1a; 需求阶段&#xff…

Java基础:接口

1.接口的概念 当不是所有子类, 而是多个子类都包含一个方法时, 为了代码书写规范性, 可以用自定义的接口来统一该方法的书写规范. 所以接口可以看作是一种书写规则. 接口是对行为的抽象 抽象类一般是书写在父类当中, 接口是单独书写的, 不是一种类 2.接口的定义和使用 3.接口…

MATLAB/Simulink 通信原理及仿真学习(一)

文章目录MATLAB/Simulink 通信原理及仿真学习&#xff08;一&#xff09;基本操作 (23.2.16)MATLAB 变量矩阵运算画图工具函数函数文件操作MATLAB/Simulink 通信原理及仿真学习&#xff08;一&#xff09; 基本操作 (23.2.16) MATLAB 变量 变量以字母开头&#xff0c;后接字…

Ubuntu16.04搭建Fabric1.4环境

一、换源 为了提高下载速度&#xff0c;将ubuntu的源改成国内的源&#xff08;推荐阿里云源和清华源&#xff09; apt源保存在 /etc/apt/sources.list / 代表根目录 /etc 这个文件夹几乎放置了系统的所有配置文件 1.备份 sudo cp /etc/apt/sources.list sources_backup.l…

媒体邀约之企业如何加强品牌的宣传力度

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。胡老师分享了许多媒体传播方面的经验&#xff0c;今天就跟大家分享下我对企业宣传方面的看法。企业如何加强品牌的宣传力度&#xff1a;1&#xff0c;网络宣传在社交媒体上建立企业账户&am…

Jmeter常用断言之响应断言详解

响应断言是最常用的一种断言方法&#xff0c;主要是对响应结果中的文本内容进行断言&#xff0c;比如响应结果是否包含指定的值&#xff0c;或者是否等于指定的值。响应断言可以适用各种返回类型的响应结果&#xff0c;如&#xff1a;Test、html、application/json、applicatio…

安装 cplex 求解器

安装 cplex 求解器 安装 cplex 求解器和python-docplexcplex 安装matlab 用户安装 cplexpython 版本安装 cplex 求解器和python-docplex cplex 安装 cplex 是解决优化问题的一个工具箱&#xff0c;用来线性规划、混合整数规划和二次规划的高性能数学规划求解器。可以理解成&a…