跳转至

数据依赖问题

我们在处理数据的时候,会出现数据之间相互依赖的场景,
比如我对一个数据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;
}

// 性能方面
// 并行处理和计算:利用多核,对不相互依赖的数据进行并行更新,以提高处理速度。
// 缓存机制:对于频繁使用的数据,使用缓存来减少重复计算和数据读取的时间。
// 设计方面
// 分层架构:将数据依赖图划分为不同的层次,每个层次只依赖于上一层的数据,简化依赖关系。
// 监控和日志:实时跟踪数据更新的状态和性能指标,以便及时发现和解决问题。