一、Linux / Shell
任务:sort / uniq 日志去重统计(sort 负责排序使相同行相邻,uniq 负责去重统计,两者组合是日志分析的黄金搭档,这是大数据 “UV/PV 统计” 的 Linux 最简实现)
对 access.log 执行:
1.取出所有 IP(第 1 列)
2.排序
3.去重并统计每个 IP 出现次数
4.按出现次数倒序排列
awk '{print $1}' access.log | sort | uniq -c | sort -nr
- sort
语法:
sort [选项] [文件...]
-n 按数值排序 sort -n file
-r 反向排序 sort -r file
-k 指定排序字段 sort -k 2 file
-t 指定字段分隔符 sort -t ':' -k 3 file
-u 去重排序 sort -u file
-o 输出到文件 sort -o output.txt file
-h 人类可读数值(1K,2M) sort -h file
-V 版本号排序 sort -V file
示例:
# 按第二列排序(默认空格分隔)
sort -k 2 access.log
# 按IP地址排序(点分隔)
sort -t '.' -k 1,1n -k 2,2n -k 3,3n -k 4,4n ips.txt
# 按多个字段排序
sort -k1,1 -k2,2n file # 先按字段1,再按字段2数值
# 去重排序
sort -u access.log > unique.log
- uniq
语法:
uniq [选项] [输入文件] [输出文件]
-c 统计重复次数 uniq -c file
-d 只显示重复行 uniq -d file
-u 只显示唯一行 uniq -u file
-i 忽略大小写 uniq -i file
-f n 跳过前n个字段 uniq -f 1 file
-s n 跳过前n个字符 uniq -s 2 file
注意:uniq只处理相邻重复行,必须先排序!
# 错误示例 - 不会去重
uniq file.txt
# 正确示例
sort file.txt | uniq
组合场景:
统计日志中IP访问次数
# 提取IP,排序,去重统计,按次数倒序
awk '{print $1}' access.log | sort | uniq -c | sort -rn
# 只显示前10个
awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -10
统计HTTP状态码
awk '{print $9}' access.log | sort | uniq -c | sort -rn
# 输出示例:
# 543 200
# 89 404
# 23 500
统计特定时间段
# 统计每小时请求数
awk '{print $4}' access.log | cut -d: -f2 | sort | uniq -c
# 统计每天请求数
awk '{print $4}' access.log | cut -d/ -f1-3 | sort | uniq -c
查找高频错误
# 统计错误日志中的错误类型
grep "ERROR" app.log | awk -F']' '{print $2}' | sort | uniq -c | sort -rn
多字段组合统计
# 统计每个IP访问每个URL的次数
awk '{print $1, $7}' access.log | sort | uniq -c | sort -rn
# 统计每小时每个状态码的数量
awk '{print $4, $9}' access.log | cut -d: -f2 | sort | uniq -c
二、SQL
今日方向:中位数 + 多表复杂统计 + 区间判断
569. 员工薪水中位数(窗口函数 + 计数,极高频)
WITH ranked AS (
SELECT
Id,
Company,
Salary,
ROW_NUMBER() OVER (PARTITION BY Company ORDER BY Salary) AS rn,
COUNT(*) OVER (PARTITION BY Company) AS cnt
FROM Employee
)
SELECT
Id,
Company,
Salary
FROM ranked
WHERE rn IN (FLOOR((cnt + 1) / 2), CEIL((cnt + 1) / 2));
中位数通用解法
奇数:取中间一条 偶数:
取中间两条
公式:rn IN ( (cnt+1)/2, (cnt+2)/2 )
窗口函数 COUNT () OVER (PARTITION BY)
不改变行数,直接获取每组总条数
避免子查询嵌套,性能更高*
FLOOR / CEIL
向下取整 / 向上取整
用于兼容奇偶两种情况
1321. 餐馆营业额计算(滑动窗口 / 累计求和)
SELECT distinct t1.visited_on,
sum(t2.amount) as amount,
round(sum(t2.amount)/7, 2) as average_amount
from customer t1 join customer t2
on datediff(t1.visited_on, t2.visited_on) between 0 and 6
group by t1.visited_on, t1.customer_id
having count(distinct t2.visited_on) = 7
order by visited_on
滑动窗口(7 日滑动求和 / 均值)
ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
表示:当前行 + 前 6 行,共 7 行
OVER (ORDER BY) 无 PARTITION
全局按时间排序,实现连续日期滑动计算
先按天聚合再滑动
同一天可能有多条记录,必须先 SUM 再做窗口
1454. 每位用户的活跃天数
SELECT
user_id,
COUNT(DISTINCT DATE(activity_time)) AS active_days
FROM Activity
GROUP BY user_id
HAVING active_days >= 3;
COUNT(DISTINCT DATE(…))
同一天多次行为只算 1 天活跃
是 DAU / 活跃用户统计标准写法
DATE()
截取datetime 的日期部分,忽略时分秒
HAVING 过滤分组结果
不能用 WHERE,因为是聚合后过滤
三、PySpark 核心新内容
缺失值处理 + 多字段聚合 + 写入文件 + 简单性能调优
直接在 PyCharm 运行:
from pyspark.sql import SparkSession
from pyspark.sql import functions as F
from pyspark.sql.types import IntegerType
spark = SparkSession.builder \
.master("local[*]") \
.appName("day7") \
.getOrCreate()
# 构造含缺失值的数据
data = [
(1, "click", 10, None),
(1, "click", 20, "2025-01-01"),
(2, "view", None, "2025-01-01"),
(3, "buy", 50, "2025-01-02")
]
df = spark.createDataFrame(data, ["uid", "event", "cost", "dt"])
# ==================== 新知识点1:缺失值处理 ====================
# 查看缺失
df.select([F.count(F.when(F.isnull(c), c)).alias(c) for c in df.columns]).show()
# 填充缺失值
df = df.fillna({"cost": 0, "dt": "2025-01-01"})
# ==================== 新知识点2:多维度聚合 ====================
df.groupBy("uid", "event") \
.agg(
F.sum("cost").alias("total_cost"),
F.avg("cost").alias("avg_cost"),
F.count("*").alias("cnt")
).show()
# ==================== 新知识点3:过滤 + 排序 ====================
df.filter(F.col("total_cost") > 0) \
.orderBy(F.desc("total_cost")) \
.show()
# ==================== 新知识点4:写入CSV(实战必备) ====================
# df.write.mode("overwrite").option("header", True).csv("./day7_output")
spark.stop()
+---+-----+----+----------+
|uid|event|cost| dt|
+---+-----+----+----------+
| 1|click| 10|2025-01-01|
| 1|click| 20|2025-01-01|
| 2| view| 0|2025-01-01|
| 3| buy| 50|2025-01-02|
+---+-----+----+----------+
1.缺失值统计 isnull
df.select([F.count(F.when(F.isnull(c), c)).alias(c) for c in df.columns])
知识点:isnull() 判断空值
作用:快速查看每列缺失率,数据开发第一步
2.fillna 按列填充
df.fillna({"cost": 0, "dt": "2025-01-01"})
知识点:不同列用不同填充策略
cost 数值型 → 填 0
dt 日期型 → 填默认日期
3.多列聚合 agg
.agg(
F.sum("cost").alias("total_cost"),
F.avg("cost").alias("avg_cost"),
F.count("*").alias("cnt")
)
知识点:一次 groupBy 做多个聚合
企业级数仓最常用写法
4.写出文件模式 overwrite/append
overwrite:覆盖
append:追加
ignore:存在则不写
面试高频:生产环境严禁随意 overwrite
小知识点:
大数据表90% 场景都要处理缺失值,面试必问
四、算法
560.和为 K 的子数组(哈希表 + 前缀和)
要求:
理解前缀和思想
能写出 O (n) 最优解
知道暴力解法为什么超时
def subarraySum(nums, k):
pre_sum = {0: 1}
cur = 0
res = 0
for num in nums:
cur += num
# 找 cur - k 出现过几次
res += pre_sum.get(cur - k, 0)
pre_sum[cur] = pre_sum.get(cur, 0) + 1
return res
前缀和公式
sum(i~j) = prefix[j] - prefix[i-1]
要求 sum = k → prefix[j] - prefix[i-1] = k
→ prefix[i-1] = prefix[j] - k
哈希表记录前缀和出现次数
O (n) 时间复杂度
空间 O (n)
为什么初始 {0:1}
处理从第 0 位开始的子数组
例如 nums [0] 直接等于 k 的情况
暴力解法 O (n²)
两层循环枚举所有区间
数据量大必超时,面试不接受
2834

被折叠的 条评论
为什么被折叠?



