数据依赖问题
我们在处理数据的时候,会出现数据之间相互依赖的场景,
比如我对一个数据m1加工之后生成的新数据取名m1plus,对这个新数据以及另外一个数据源m1a合并再次加工生成的数据m1plus2。
那如果我对原始的m1数据和m1a数据进行更新之后,就要按照顺序分别去更新m1plus然后更新m1plus2。
也就是程序输出要给出建议数据更新顺序m1,m1plus,m1a,m1plus2
但,用户有时候在处理数据的时候,会不小心写错依赖,比如将上面的m1数据填上了对m1plus2的依赖,这个时候程序需要提醒用户出错。
文件deps.json和deps2.json分别是两个用户提交的数据依赖请求,json数据,key代表数据名字,
value代表数据名字更新所需要依赖的子数据集名称。如果用户填写的依赖没有问题,输出建议用户更新数据的顺序,
如果有问题,提醒用户哪个数据集依赖出错了。
请在linux环境下,实现c++版本的处理这个需求的代码。并且计时剔除数据读取和写入的时间。
附加思考:
实际生产环境中,数据集成千上万个甚至达到百万级别的数据依赖集合的场景,对于处理这个级别的数据系统在性能和设计上有什么建议。
#include <algorithm>
#include <cctype>
#include <chrono>
#include <fstream>
#include <iostream>
#include <queue>
#include <sstream>
#include <stack>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
using namespace std;
// 仅支持本问题所需的键值对格式的JSON解析器
unordered_map<string, vector<string>> parseJSON(const string& content) {
unordered_map<string, vector<string>> result;
string current_key;
vector<string> current_values;
enum State {
START,
IN_KEY,
AFTER_KEY,
IN_VALUE,
IN_ARRAY,
IN_VALUE_STR
} state = START;
string buffer;
bool escape = false;
for (char c : content) {
if (escape) {
buffer += c;
escape = false;
continue;
}
switch (state) {
case START:
if (c == '{')
state = START;
else if (isspace(c))
continue;
else if (c == '"')
state = IN_KEY;
break;
case IN_KEY:
if (c == '"') {
current_key = buffer;
buffer.clear();
state = AFTER_KEY;
} else if (c == '\\')
escape = true;
else
buffer += c;
break;
case AFTER_KEY:
if (isspace(c))
continue;
else if (c == ':')
state = IN_ARRAY;
break;
case IN_ARRAY:
if (isspace(c))
continue;
else if (c == '[')
state = IN_ARRAY;
else if (c == ']') {
if (!buffer.empty()) {
current_values.push_back(buffer);
buffer.clear();
}
result[current_key] = current_values;
current_values.clear();
state = START;
} else if (c == '"')
state = IN_VALUE_STR;
break;
case IN_VALUE_STR:
if (c == '"') {
current_values.push_back(buffer);
buffer.clear();
state = IN_ARRAY;
} else if (c == '\\')
escape = true;
else
buffer += c;
break;
default:
break;
}
}
return result;
}
unordered_map<string, vector<string>> readDependencies(const string& filename) {
ifstream file(filename);
if (!file.is_open()) {
throw runtime_error("无法打开文件: " + filename);
}
string content((istreambuf_iterator<char>(file)),
istreambuf_iterator<char>());
return parseJSON(content);
}
vector<string> processDependencies(
const unordered_map<string, vector<string>>& deps) {
// 构建反向依赖图和入度表
unordered_map<string, vector<string>> reverse_graph;
unordered_map<string, int> in_degree;
// 初始化所有节点的入度为0
for (const auto& pair : deps) {
in_degree[pair.first] = 0;
}
// 构建反向图并计算入度
for (const auto& pair : deps) {
const string& node = pair.first;
for (const string& dep : pair.second) {
// 检查依赖是否存在
if (deps.find(dep) == deps.end()) {
throw runtime_error("依赖错误: " + node + " 依赖于不存在的节点 " + dep);
}
reverse_graph[dep].push_back(node);
in_degree[node]++;
}
}
// Kahn's算法进行拓扑排序
queue<string> q;
for (const auto& pair : in_degree) {
if (pair.second == 0) {
q.push(pair.first);
}
}
vector<string> result;
result.reserve(deps.size());
while (!q.empty()) {
string node = q.front();
q.pop();
result.push_back(node);
for (const string& neighbor : reverse_graph[node]) {
if (--in_degree[neighbor] == 0) {
q.push(neighbor);
}
}
}
// 检查是否有循环依赖
if (result.size() != deps.size()) {
unordered_set<string> processed(result.begin(), result.end());
string cycle_nodes;
for (const auto& pair : deps) {
if (processed.find(pair.first) == processed.end()) {
if (!cycle_nodes.empty()) cycle_nodes += ", ";
cycle_nodes += pair.first;
}
}
throw runtime_error("存在循环依赖: " + cycle_nodes);
}
return result;
}
int main(int argc, char* argv[]) {
if (argc != 2) {
cerr << "用法: " << argv[0] << " <dependency_file.json>" << endl;
return 1;
}
try {
auto deps = readDependencies(argv[1]);
auto start = chrono::high_resolution_clock::now();
auto order = processDependencies(deps);
auto end = chrono::high_resolution_clock::now();
chrono::duration<double, micro> elapsed = end - start;
cout << "建议的更新顺序:" << endl;
for (const string& node : order) {
cout << node << endl;
}
cerr << "依赖处理时间: " << elapsed.count() << " 微秒" << endl;
} catch (const exception& e) {
cerr << "错误: " << e.what() << endl;
return 1;
}
return 0;
}
// 性能方面
// 并行处理和计算:利用多核,对不相互依赖的数据进行并行更新,以提高处理速度。
// 缓存机制:对于频繁使用的数据,使用缓存来减少重复计算和数据读取的时间。
// 设计方面
// 分层架构:将数据依赖图划分为不同的层次,每个层次只依赖于上一层的数据,简化依赖关系。
// 监控和日志:实时跟踪数据更新的状态和性能指标,以便及时发现和解决问题。