最近在做一个国内地震数据的可视化分析项目,用到了Python Flask框架和Hive on Spark技术栈。这个系统主要解决地震数据量大、查询分析慢的问题,同时提供直观的可视化展示。下面我会详细分享整个系统的架构设计和实现过程。
整个系统采用前后端分离的架构:
选择这个架构主要考虑以下几点:
提示:Hive on Spark特别适合处理这种时空数据,因为Spark的分布式计算能力可以很好地处理大规模数据,而Hive提供了SQL接口,方便数据查询。
选择Flask作为后端框架有几个原因:
python复制from flask import Flask, jsonify
app = Flask(__name__)
@app.route('/api/earthquake', methods=['GET'])
def get_earthquake_data():
# 查询逻辑
return jsonify(data)
Hive on Spark的配置是关键,我们使用的是CDH6.3.2版本,主要配置参数:
xml复制<property>
<name>hive.execution.engine</name>
<value>spark</value>
</property>
<property>
<name>spark.master</name>
<value>yarn</value>
</property>
<property>
<name>spark.executor.memory</name>
<value>8g</value>
</property>
地震数据表结构设计:
sql复制CREATE TABLE earthquake_data (
id STRING,
time TIMESTAMP,
latitude DOUBLE,
longitude DOUBLE,
depth DOUBLE,
magnitude DOUBLE,
location STRING
)
PARTITIONED BY (year INT, month INT)
STORED AS ORC;
分区设计采用年月分区,这样查询特定时间段的数据时可以有效减少扫描量。
地震数据来源于中国地震台网中心,原始数据是CSV格式。我们开发了数据导入工具:
python复制from pyspark.sql import SparkSession
spark = SparkSession.builder \
.appName("Earthquake Data Import") \
.enableHiveSupport() \
.getOrCreate()
df = spark.read.csv("hdfs://path/to/raw/data.csv", header=True)
df.write.mode("append").insertInto("earthquake_data")
注意:实际导入时要处理数据质量问题,比如空值、异常值等。
针对常见的查询场景,我们做了以下优化:
python复制# 缓存常用查询
spark.sql("CACHE TABLE recent_earthquakes AS SELECT * FROM earthquake_data WHERE year=2023")
前端通过RESTful API获取数据,主要接口设计:
| 接口路径 | 方法 | 参数 | 描述 |
|---|---|---|---|
| /api/earthquake/query | GET | start_time, end_time, min_magnitude | 查询地震数据 |
| /api/earthquake/stats | GET | region, time_range | 获取统计信息 |
| /api/earthquake/heatmap | GET | zoom_level, bbox | 获取热力图数据 |
后端接口实现示例:
python复制@app.route('/api/earthquake/query')
def query_earthquake():
start_time = request.args.get('start_time')
end_time = request.args.get('end_time')
min_magnitude = float(request.args.get('min_magnitude', 0))
sql = f"""
SELECT * FROM earthquake_data
WHERE time BETWEEN '{start_time}' AND '{end_time}'
AND magnitude >= {min_magnitude}
"""
result = spark.sql(sql).toJSON().collect()
return jsonify(result)
使用Mapbox GL JS实现地图展示,配合ECharts实现热力图:
javascript复制// 初始化地图
const map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/dark-v10',
center: [104.0, 35.0],
zoom: 4
});
// 加载热力图数据
fetch('/api/earthquake/heatmap')
.then(response => response.json())
.then(data => {
const heatmap = new HeatmapOverlay({
radius: 15,
maxOpacity: 0.6,
gradient: { ... }
});
heatmap.setData(data);
});
使用ECharts实现地震时间分布图:
javascript复制option = {
xAxis: {
type: 'time'
},
yAxis: {
type: 'value'
},
series: [{
data: [],
type: 'line',
smooth: true
}]
};
// 动态更新数据
function updateChart() {
fetch('/api/earthquake/stats')
.then(response => response.json())
.then(data => {
myChart.setOption({
series: [{
data: data
}]
});
});
}
查询优化:
内存配置:
bash复制spark-submit --executor-memory 8g --driver-memory 4g ...
python复制# 对倾斜键加盐处理
df = df.withColumn("salted_key", concat(col("key"), lit("_"), (rand() * 10).cast("int")))
我们使用Prometheus + Grafana监控系统运行状态:
系统采用Docker Compose部署,主要服务包括:
yaml复制version: '3'
services:
spark-master:
image: bitnami/spark:3.3
ports:
- "8080:8080"
environment:
- SPARK_MODE=master
spark-worker:
image: bitnami/spark:3.3
depends_on:
- spark-master
environment:
- SPARK_MODE=worker
- SPARK_MASTER_URL=spark://spark-master:7077
flask-app:
build: ./flask
ports:
- "5000:5000"
depends_on:
- spark-master
- mysql
系统上线后,查询性能有了显著提升:
| 查询类型 | 优化前 | 优化后 |
|---|---|---|
| 全年数据统计 | 45s | 3.2s |
| 区域热力图 | 28s | 1.5s |
| 时间序列查询 | 32s | 2.8s |
可视化效果也得到了用户好评,特别是时间轴动画功能,可以直观展示地震活动随时间的变化。
这个项目让我对大数据处理有了更深的理解,特别是在处理时空数据方面积累了不少经验。如果再做类似项目,我会考虑使用GeoMesa等时空数据库来进一步提升查询效率。