spark 实现百分位数计算

因工作需要计算百分位数,但Spark Core不支持。介绍两种实现方式:一是用Spark SQL计算,给出示例代码及函数说明;二是自定义Spark Core计算百分位数的方法,给出代码实现、参数解释及使用示例,返回分段值数组。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

spark实现百分位数计算

工作需要 要计算百分位数 但是 spark core 不支持计算 百分位数
一.可以使用 spark sql 计算 但是 他操作起来 很不方便
select percentile_approx(tb_sku.sku_stddv,cast(array(0.2, 0.40, 0.60, 0.80) as array)) from tb_sku

其中 percentile_approx(tb_sku.sku_stddv,cast(array(0.2, 0.40, 0.60, 0.80) as array))
计算百分位数的函数
具体参数 参看:
https://blog.youkuaiyun.com/sinat_27339001/article/details/52189843
二.自定义 spark core 计算百分位数方法 当做工具使用
具体代码实现:

/**
* 计算 分位数 的分段值
*
* @param data : rdd
* @param tile : 分位数
* @return 分段值
*/

def computePercentile(data: RDD[Double], tile: Double): Double = {

if (!data.isEmpty()) {
  val r = data.sortBy(x => x)
  val c = r.count()
  if (c == 1) r.first()
  else {
    val n = (tile / 100d) * (c + 1d)
    val k = math.floor(n).toLong
    val d = n - k
    if (k <= 0) r.first()
    else {
      val index = r.zipWithIndex().map(_.swap)
      val last = c
      if (k >= c) {
        index.lookup(last - 1).head
      } else {
        index.lookup(k - 1).head + d * (index.lookup(k).head - index.lookup(k - 1).head)
      }
    }
  }
}
else 0

}

参数解释:
data :RDD[(Double)] 需要计算分位数的 RDD,
tile: Double 具体计算的分位数(20,40,60===》20%,40%。。。)
返回值:返回该RDD 的百分位数的 分段值
例如:val segment_arr = Array(utils.computePercentile(mratioRDD, 20),
utils.computePercentile(mratioRDD, 40),
utils.computePercentile(mratioRDD, 60),
utils.computePercentile(mratioRDD, 80),
utils.computePercentile(mratioRDD, 100)
)
返回 一个分段值的 数组:

0.4295004004115095
0.6420823735288389
0.8625984512540754
1.0974667335218444
4.533219776740614

<think>好的,我需要帮助用户设计一个基于Spring Boot的大数据量每日统计计算功能。首先,我需要回顾用户提供的引用内容,看看有哪些现有的技术和方案可以借鉴。 用户提到引用[1]和[5]中使用了RedisBitMap来处理每日签到统计,节省空间且高效。这可能适用于用户统计,比如UV、PV等。引用[2]讨论了分布式计算,将任务拆分并行处理,适合处理大数据量。引用[3]中的Spark用于实时和离线统计,可能涉及批处理和流处理。引用[4]提到Elasticsearch用于数据查询,可能用于存储和检索统计结果。接下来,用户的需求是使用Spring Boot实现每日统计,需要考虑大数据量下的性能。需要结合这些引用中的技术,比如RedisBitMap做实时统计,分布式任务处理分片数据,可能用SpringBatch做批处理,或者集成Spark。首先,确定统计的数据类型,比如是用户行为日志还是交易数据。如果是每日统计,可能需要定时任务触发,比如使用Spring Scheduler。大数据量的话,分片处理是关键,可以用多线程或分布式任务框架,如SpringCloudTask。 然后,数据存储方面,原始数据可能存储在数据库或日志文件中,处理后的统计结果可能需要高效存储,比如使用Elasticsearch方便查询,或者列式存储如Parquet。引用[5]中的RedisBitMap适合做实时统计,比如每日签到,但如果是更复杂的统计,可能需要批处理。容错机制也很重要,比如使用SpringRetry处理失败任务,或者检查点机制保存处理进度。引用[3]中提到Spark的容错和任务调度,可能可以借鉴。需要整合这些技术到Spring Boot中,比如配置Redis连接,设置定时任务,使用SpringBatch进行批处理,或者集成Spark进行分布式计算。同时,考虑性能优化,如JVM调优、异步处理等。 可能的问题包括如何选择实时处理框架(如Flinkvs SparkStreaming),如何保证数据一致性,存储优化等。需要根据用户的具体场景调整,比如高并发下的实时统计用Redis,离线用批处理。最后,代码示例需要展示核心部分,比如定时任务、分片处理、Redis BitMap操作,以及结果存储。确保代码符合Spring Boot的惯例,并加入必要的注释说明。</think>### 基于Spring Boot的大数据量每日统计实现方案 #### 一、架构设计原则 1. **分层处理架构**:采用流处理+批处理混合模式,实时统计使用Redis BitMap[^5],离线计算使用Spring Batch[^3] 2. **数据分片策略**:按时间窗口(如小时级)或哈希值分片,结合分布式锁保证分片完整性[^2] 3. **计算存储分离**:原始数据存储于HDFS/HBase,统计结果存储到Elasticsearch[^4] #### 二、核心实现步骤 ##### 1. 数据采集与预处理 ```java // 使用AOP记录用户行为日志 @Aspect @Component public class StatAspect { @Around("@annotation(statPoint)") public Object logStat(ProceedingJoinPoint pjp) { long start = System.currentTimeMillis(); Object result = pjp.proceed(); // 异步写入Kafka kafkaTemplate.send("user_behavior", buildLogEntry(pjp, result, System.currentTimeMillis()-start)); return result; } } ``` ##### 2. 实时统计实现(Redis BitMap) ```java // 每日UV统计 public class DailyUVCounter { private static final String KEY_PREFIX = "uv:daily:"; @Autowired private RedisTemplate<String, Object> redisTemplate; public void recordUV(Long userId) { String key = KEY_PREFIX + LocalDate.now().toString(); redisTemplate.opsForValue().setBit(key, userId, true); } public Long getUV(LocalDate date) { String key = KEY_PREFIX + date.toString(); return redisTemplate.execute((RedisCallback<Long>) conn -> conn.bitCount(key.getBytes())); } } ``` ##### 3. 离线批处理(Spring Batch) ```properties # application-batch.properties spring.batch.job.names=dailyStatJob spring.batch.initialize-schema=always ``` ```java @Configuration public class BatchConfig { @Bean public Job dailyStatJob(JobBuilderFactory jobs, StepBuilderFactory steps) { return jobs.get("dailyStatJob") .start(statStep(steps)) .build(); } @Bean public Step statStep(StepBuilderFactory steps) { return steps.get("statStep") .<LogEntry, StatResult>chunk(10000) .reader(logReader()) .processor(statProcessor()) .writer(statWriter()) .build(); } // 自定义Reader/Processor/Writer实现... } ``` #### 三、性能优化关键技术 1. **位运算加速**:使用Redis BitMap进行布尔型统计,存储密度可达$1 \text{bit/user}$[^5] $$ \text{存储空间} = \frac{N_{\text{users}}}{8} \text{ bytes} $$ 2. **列式存储优化**:统计结果采用Parquet格式存储,压缩比可达10:1 3. **向量化计算**:使用SIMD指令优化数值计算循环 ```java // 使用JNI调用Intel MKL库 public native void vectorAdd(double[] a, double[] b, double[] result); ``` #### 四、部署与监控 1. **分布式任务调度**:采用分片策略实现水平扩展 ```java @Scheduled(cron = "0 0 3 * * ?") // 每日凌晨3点执行 @ShardingTask( shardingTotalCount = 10, shardingItemParameters = "0=0-999,1=1000-1999...") public void executeDailyStat() { // 分片逻辑实现... } ``` 2. **监控指标**: - 统计延迟:使用Micrometer采集处理时间百分位数 - 数据完整性:校验统计结果总和与原始数据量的一致性[^3] #### 五、容错机制设计 1. **检查点机制**:每处理10000条记录保存一次进度 2. **重试策略**:采用指数退避重试算法 $$ \text{重试间隔} = \text{base} \times 2^{n-1} $$ 3. **最终一致性**:通过补偿任务修复数据偏差
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值