Grist prend en charge le résumé de l'expérience de stockage mysql

        Grist est un outil qui prend en charge l'importation Excel, le traitement des données, l'analyse et la visualisation des données. Le corps principal est implémenté dans le langage node ts et ses fonctions sont similaires à celles d'Excel. Sécurité du traitement des données. Les données sont stockées dans sqlite3 et l'interface api de sqlite3 est implémentée par C++, qui est une interface api personnalisée et prend en charge les fonctions de données DDL, CURD, de transaction et de sauvegarde.

        L'objectif cette fois est de modifier son stockage de base de données vers mysql8.Cette modification n'est pas aussi simple que de changer la connexion à la base de données, mais nécessite de nombreuses modifications et ajustements détaillés.

        Il est nécessaire de bien comprendre les différences entre sqlite3 et mysql8, telles que : il existe des différences dans l'expression des instructions SQL, principalement dans :

        1. La différence de type de données nécessite la modification de l'instruction de création de table, c'est-à-dire l'instruction DDL, qui se reflète principalement dans le fait que certains textes doivent être modifiés en Varchar(256), qui prend principalement en charge

        2. L'instruction CURD doit être modifiée ;

        3. Il faut implémenter l'interface mysql selon l'interface sqlite3, notamment :

        a. Vous devez choisir une interface mysql qui supporte la promesse. En comparant mysql, mysql2 et mysqlx, vous devez finalement utiliser mysql2/promise

        b. Il est nécessaire de rassembler les données interrogées, car les données sqlite3 sont rassemblées par défaut dans l'interface C++, de sorte que les données peuvent être affichées normalement dans l'eau, c'est-à-dire que les données sont rassemblées dans le type Buffer.

        c. Besoin de prendre en charge les transactions, les transactions ne prennent pas en charge la mise en œuvre directe via les commandes sql, mais utilisent la méthode de connexion transactionnelle ;

        d. Les données interrogées doivent être converties dans un format de données similaire à sqlite3, afin d'obtenir directement la valeur du champ de données, tel que : type ResultRow ;

        e. En termes d'insertion de données, afin d'améliorer les performances, il est nécessaire d'utiliser des tableaux pour l'insertion par lots, et les performances peuvent atteindre plus de 20 000 enregistrements/seconde ;

         F. En termes de mise à jour des données, afin d'améliorer les performances, il est nécessaire d'utiliser la méthode de mise à jour par lots pour mettre à jour, et les performances peuvent atteindre plus de 100 000 enregistrements/seconde ;

         g. Conversion des instructions SQL, vous devez faire attention à la conversion des mots-clés tels que le nom de la table et le nom du champ ", groupe, etc., données de type Json "{}", vous devez changer les guillemets doubles en guillemets simples '{ }' pour le stockage ;

         h. La sauvegarde des données ne peut pas utiliser l'outil sqldump, mais ne peut être réalisée que par table via sql (efficacité inférieure à sqldump).

    Joignez quelques codes de base pour référence (y compris transaction, insertion par lot, mise à jour par lot, requête unique, requête par lot, sauvegarde, marsall par lot) :

public async getConnection(doc_name:string):Promise<mysql.Connection>
  {
    const host = process.env.MYSQL_HOST?process.env.MYSQL_HOST:'localhost'
    const port = process.env.MYSQL_PORT?process.env.MYSQL_PORT:'3310'
    const user = process.env.MYSQL_USER?process.env.MYSQL_USER:'root'
    const password = process.env.MYSQL_PASSWORD?process.env.MYSQL_PASSWORD:'123456789'
    const database = process.env.MYSQL_DATABASE?process.env.MYSQL_DATABASE:'mysql'
    var conn = mysql.createConnection(`mysql://${user}:${password}@${host}:${port}/${database}`);
    (await conn).execute("create database if not exists `"+doc_name+"`");
    (await conn).end();
    return mysql.createConnection(`mysql://${user}:${password}@${host}:${port}/${doc_name}?multipleStatements=true`);
  }
public async backup(src:string, dest:string):Promise<void>{
      const dest_name =  replaceAll(src+'-',"",dest);
      console.log('backup      src:',src);
      console.log('backup      dest:',dest);
      console.log('backup:dest_name:',dest_name);
      const dest_db =  MySQLDB.openDBRaw(dest);
      //获取所有表
      const tblRows = await this.all(`show tables from ${this.doc_name}`)
      for (const tblRow of tblRows) {
        for (const key in tblRow){
          const createTblRows =  await this.all(`show create table ${tblRow[key]}`)
          for (const createTbl of createTblRows){
            for (const key2 in createTbl){
              if (createTbl[key2]!=tblRow[key])
              {
                const create_sql = createTbl[key2];
                await (await dest_db).exec(create_sql);
                const sql = `insert into \`${dest_name}\`.${tblRow[key]} select * from ${tblRow[key]}`;
                await this.exec(sql);
              }
            }
          }
        }
      }
      return
    }
public async run(sql: string, ...params: any[]): Promise<mysql.OkPacket[]> {
    const db = await this.getConnection(this.doc_name)
    sql = replaceAll("\"group\"","`group`",sql);
    sql = replaceAll(" group ","`group`",sql);
    sql = replaceAll("group=","`group`=",sql);  //mysql 中group为关键字
    sql = replaceAll("1e999","0",sql);
    console.log('run sql:',sql, 'params:',params);
    var rows:mysql.OkPacket[]
    if (params.length>0)
      if (typeof params[0]==='object')
        [rows,,] =await db.query(sql,params[0]);
      else
        [rows,,] =await db.query(sql,params);

    else
      [rows,,] =await db.query(sql);
    //console.log('rows:',rows)
    //console.log('fields:',fields)
    db.destroy()
    return rows;
  }
public async get(sql:string, ...params: any[]):Promise<ResultRow> {
      const db = await this.getConnection(this.doc_name)
        console.log('get sql:',sql, 'params:',params);
        var rows:mysql.RowDataPacket[]
        if (params.length>0)
        {
          [rows,,] = (await db.query(sql,params));
        }
        else{
          [rows,,] = (await db.query(sql));
        }
        //console.log('rows:',rows)
        //console.log('fields:',fields)
        var row:ResultRow = {};
        for(var line of rows)
        {
          row = line;
        }
        // console.log('get row:',row);
        db.destroy()
        return row;
    }
public async all(sql: string, ...params: any[]): Promise<ResultRow[]> {
      const db = await this.getConnection(this.doc_name)
        //console.log('all sql:',sql, 'params:',params);
        var rows:mysql.RowDataPacket[]
        if (params.length>0)
        {
          [rows, ,] = (await db.query(sql,params));
        }
        else{
          [rows, ,]  = (await db.query(sql));
        }
        //console.log('fields:',fields)
        var table:ResultRow[]=[];
        for (var line of rows)
        {
            var row:ResultRow ={}
            row = line;
            table.push(row);
        }
        //console.log('all table:',table)
        db.destroy()
        return table;
    }
public async allMarshal(sql: string, ...params: any[]): Promise<Buffer> {
      const db = await this.getConnection(this.doc_name)
      console.log('allMarshal sql:',sql, 'params:',params);
      var rows:mysql.RowDataPacket[], fields:mysql.FieldPacket[]
      if (params.length>0&&params[0].length>0)
      {
        [rows, fields] = (await db.query(sql, params));
      }
      else{
        [rows, fields] = (await db.query(sql));
      }
      //console.log('allMarshal fields:',fields)
      const marshaller = new marshal.Marshaller({version: 2});
      var table:{[key:string]:any[]} = {}
      if (rows.length>0)
      {
        for(var line of rows)
        {
            for(var col of fields)
            {
              var col_value = line[col.name];
              var values = table[col.name];
              if (values === undefined)
                values=[]
              if (Buffer.isBuffer(col_value))//解决BLOB的解码问题
                if (isNumber(col_value.toString()))
                  values.push(Number(col_value.toString()))
                else
                  values.push(col_value.toString())
              else
                values.push(col_value);
              table[col.name]= values;
            }
        }
      }
      else
      {
        for(var col of fields)
          {
            table[col.name]= [];
          }
      }
      marshaller.marshal(table);
      const buf = marshaller.dump();
      db.destroy()
      return Buffer.from(buf);
  }
public async prepare(sql: string, ...params: any[]): Promise<any> {
    const db = await this.getConnection(this.doc_name)
    sql = replaceAll("\"group\"","`group`",sql);
    sql = replaceAll(" group ","`group`",sql);
    sql = replaceAll("group=","`group`=",sql);  //mysql 中group为关键字
    sql = replaceAll("1e999","0",sql);
    console.log('prepare sql:',sql, 'params[0].length:',params[0].length);
    var start = process.uptime();
    var end = process.uptime();
    var begin = process.uptime();
    var diff = 1;
    if (params.length>0)
        //批量插入"INSERT INTO TABLE() VALUES ?",其中?是个包含数组的数组[params[0]]
        if (/INSERT INTO/.test(sql))
        {
          await db.query(sql,[params[0]]);
        }
        else//批量更新
        {
          if (typeof params[0]==='object')
          {
            //sql likes "update table field=? where id in ?"
            //toparams likes [`case id when 1 then "A" end`, [[1,2,3]]]
            const UPDATE_NUM = 200;
            var field_cases:{[key:string]:string}={}
            var id_ins = []
            var i = 0
            var left = 0
            var toparams =[]
            for(var param of params[0])
            {
              var fields:{[key:string]:string|number|boolean} = param[0];
              var ids:{[key:string]:number} = param[1];
              id_ins.push(ids['id']);
              for (var key in fields)
              {
                if (field_cases[key] === undefined)
                {
                  if (typeof fields[key] ==='string')
                    field_cases[key] = `case id when ${ids['id']} then '${fields[key]}' `
                  else
                    field_cases[key] = `case id when ${ids['id']} then ${fields[key]} `
                }
                else
                {
                  if (typeof fields[key] ==='string')
                    field_cases[key] = field_cases[key].concat(`when ${ids['id']} then '${fields[key]}' `)
                  else
                    field_cases[key] = field_cases[key].concat(`when ${ids['id']} then ${fields[key]} `)
                }
              }
              i = i + 1;
              left = i%UPDATE_NUM;
              if (left===0)
              {
                for (var key in field_cases)
                {
                  toparams.push(field_cases[key].concat(' end'));
                }
                toparams.push([id_ins]);
                sql = mysql.format(sql, toparams);
                sql = replaceAll("\'case","case",sql);
                sql = replaceAll("end\'","end",sql);
                sql = replaceAll("\\","",sql);
                //console.log('sql:',sql);
                await db.query(sql);
                end = process.uptime()
                diff = end-start
                //console.log('提交 i:',i, '执行速度:', Math.round(UPDATE_NUM/diff),'/秒');
                start = process.uptime();
                field_cases = {}
                toparams = []
                id_ins = []
              }
            }
            //剩下的
            if (left>0)
            {
              for (var key in field_cases)
              {
                toparams.push(field_cases[key].concat(' end'));
              }
              toparams.push([id_ins]);
              sql = mysql.format(sql, toparams);
              sql = replaceAll("\'case","case",sql);
              sql = replaceAll("end\'","end",sql);
              sql = replaceAll("\\","",sql);
              //console.log('sql:',sql);
              await db.query(sql);
              end = process.uptime()
              diff = end-start
              //console.log('提交 i:',i, '执行速度:', Math.round(left/diff),'/秒');
            }
          }
          else
          {
            console.log('params:',params)
            await db.execute(sql,params);
          }
        }
    else
      await db.query(sql);
    end = process.uptime()
    diff = end-begin
    console.log('prepare sql:', params[0].length, '执行速度:',  Math.round(params[0].length/diff),'/秒');
    db.destroy()
    return
  }
private async _execTransactionImpl<T>(callback: () => Promise<T>): Promise<T> {
    // We need to swallow errors, so that one failed transaction doesn't cause the next one to fail.
    await this._prevTransaction.catch(noop);
    const db = await this.getConnection(this.doc_name)
    await db.beginTransaction()
    try {
      const value = await callback();
      await db.commit()
      return value;
    } catch (err) {
      try {
        await db.rollback()
      } catch (rollbackErr) {
        log.error("MySQLDB[%s]: Rollback failed: %s", this._dbPath, rollbackErr);
      }
      db.destroy()
      throw err;    // Throw the original error from the transaction.
    }
  }

Je suppose que tu aimes

Origine blog.csdn.net/wxl781227/article/details/126642747
conseillé
Classement