2018-10-25 09:20:14 2197次浏览 3条回答 0 悬赏 10 金钱

对Model进行查询、保存、删除操作时速度很慢,从Debug中看到很多query

SELECT
    d.nspname AS table_schema,
    c.relname AS table_name,
    a.attname AS column_name,
    COALESCE(td.typname, tb.typname, t.typname) AS data_type,
    COALESCE(td.typtype, tb.typtype, t.typtype) AS type_type,
    a.attlen AS character_maximum_length,
    pg_catalog.col_description(c.oid, a.attnum) AS column_comment,
    a.atttypmod AS modifier,
    a.attnotnull = false AS is_nullable,
    CAST(pg_get_expr(ad.adbin, ad.adrelid) AS varchar) AS column_default,
    coalesce(pg_get_expr(ad.adbin, ad.adrelid) ~ 'nextval',false) AS is_autoinc,
    CASE WHEN COALESCE(td.typtype, tb.typtype, t.typtype) = 'e'::char
        THEN array_to_string((SELECT array_agg(enumlabel) FROM pg_enum WHERE enumtypid = COALESCE(td.oid, tb.oid, a.atttypid))::varchar[], ',')
        ELSE NULL
    END AS enum_values,
    CASE atttypid
         WHEN 21 /*int2*/ THEN 16
         WHEN 23 /*int4*/ THEN 32
         WHEN 20 /*int8*/ THEN 64
         WHEN 1700 /*numeric*/ THEN
              CASE WHEN atttypmod = -1
               THEN null
               ELSE ((atttypmod - 4) >> 16) & 65535
               END
         WHEN 700 /*float4*/ THEN 24 /*FLT_MANT_DIG*/
         WHEN 701 /*float8*/ THEN 53 /*DBL_MANT_DIG*/
         ELSE null
      END   AS numeric_precision,
      CASE
        WHEN atttypid IN (21, 23, 20) THEN 0
        WHEN atttypid IN (1700) THEN
        CASE
            WHEN atttypmod = -1 THEN null
            ELSE (atttypmod - 4) & 65535
        END
           ELSE null
      END AS numeric_scale,
    CAST(
             information_schema._pg_char_max_length(information_schema._pg_truetypid(a, t), information_schema._pg_truetypmod(a, t))
             AS numeric
    ) AS size,
    a.attnum = any (ct.conkey) as is_pkey,
    COALESCE(NULLIF(a.attndims, 0), NULLIF(t.typndims, 0), (t.typcategory='A')::int) AS dimension
FROM
    pg_class c
    LEFT JOIN pg_attribute a ON a.attrelid = c.oid
    LEFT JOIN pg_attrdef ad ON a.attrelid = ad.adrelid AND a.attnum = ad.adnum
    LEFT JOIN pg_type t ON a.atttypid = t.oid
    LEFT JOIN pg_type tb ON (a.attndims > 0 OR t.typcategory='A') AND t.typelem > 0 AND t.typelem = tb.oid OR t.typbasetype > 0 AND t.typbasetype = tb.oid
    LEFT JOIN pg_type td ON t.typndims > 0 AND t.typbasetype > 0 AND tb.typelem = td.oid
    LEFT JOIN pg_namespace d ON d.oid = c.relnamespace
    LEFT JOIN pg_constraint ct ON ct.conrelid = c.oid AND ct.contype = 'p'
WHERE
    a.attnum > 0 AND t.typname != ''
    AND c.relname = 'current_user'
    AND d.nspname = 'public'
ORDER BY
    a.attnum;
select
    ct.conname as constraint_name,
    a.attname as column_name,
    fc.relname as foreign_table_name,
    fns.nspname as foreign_table_schema,
    fa.attname as foreign_column_name
from
    (SELECT ct.conname, ct.conrelid, ct.confrelid, ct.conkey, ct.contype, ct.confkey, generate_subscripts(ct.conkey, 1) AS s
       FROM pg_constraint ct
    ) AS ct
    inner join pg_class c on c.oid=ct.conrelid
    inner join pg_namespace ns on c.relnamespace=ns.oid
    inner join pg_attribute a on a.attrelid=ct.conrelid and a.attnum = ct.conkey[ct.s]
    left join pg_class fc on fc.oid=ct.confrelid
    left join pg_namespace fns on fc.relnamespace=fns.oid
    left join pg_attribute fa on fa.attrelid=ct.confrelid and fa.attnum = ct.confkey[ct.s]
where
    ct.contype='f'
    and c.relname='current_user'
    and ns.nspname='public'
order by
    fns.nspname, fc.relname, a.attnum
'enableSchemaCache' => true //这里设置为true或false都试过了

诸如此类的sql query,仅对2个postgresql数据库建立事务、设置事务隔离性,对3张数据表用实体(ActiveRecord)执行2-4条数据的插入、删除,这一过程耗费了4秒时间;当我不使用model操作时,用纯sql command命令去执行,这些sql query减少了三分之二,速度非常快。请问如何设置关闭这些query的出现呢?

补充于 2018-10-25 09:30

贴上部分代码


$datas = User::find()->where(['type' => $type])->with('user_type')->all();

if ($datas){
    ...
    $transaction = User::getDb()->beginTransaction();
    $transaction->setIsolationLevel(\yii\db\Transaction::SERIALIZABLE);
    try {
        $transaction2 = Customer::getDb()->beginTransaction();
        $transaction2->setIsolationLevel(\yii\db\Transaction::SERIALIZABLE);
        try {
           foreach ($datas as $key => $user) {
                ...
                $data = $data2 = $user->getAttributes();
                ...
                $model = new UserInfo();
                $model2 = new Customer();
                $model->load($data, "");
                $model2->load($data2, "");
                if($model->save() && $model2->save() && $user->delete()){
                    $transaction2->commit();
                    $transaction->commit();
                }
            }
        } catch(\Throwable $e) {
            $transaction2->rollBack();
            throw $e;
        }
    } catch(\Throwable $e) {
        $transaction->rollBack();
        throw $e;
    }
}

最佳答案

  • 回答于 2018-10-25 15:31 举报

    不知道 sqlcommand 执行和 AR 执行的代码怎么写的。你多贴一点代码瞅瞅。

    4 条回复
    回复于 2018-10-25 17:33 回复

    model的操作基本上就是上面贴的代码,sqlCommand语句是直接Customer->getDb()->createCommand()->insert('customer',data2)->execute();这样去操作。我现在是$model还是按照原先的操作,$model2的操作就换成sqlCommand了,当循环有12条数据时,不会像之前那样需要12秒了,现在仅需1-2秒左右。

    回复于 2018-10-25 17:42 回复

    初步怀疑是,在一个循环中,对两个数据库连接执行插入和删除的操作,导致yii数据表缓存丢失,每次操作需要重新通过Schema加载数据表结构再进行操作。仅仅是怀疑而已。当循环有12条数据时,我看到Debug中包括插入和删除总共有165条记录,query占了大部分。而如果都使用sqlCommand方式来操作,大概有40多条数据库操作记录,这是正确的,query语句仅查询几次,其他都是插入、删除、set事务以及事务隔离性。

    回复于 2018-10-25 23:23 回复

    Schema不删除缓存是不会重新加载的。应该还是代码要优化,sqlCommand 好点的话,就用sqlCommand

    回复于 2018-10-26 15:35 回复

    今天本地测试了1000条数据操作,需要65秒,蛋疼。
    通过Debug语句,实际业务中,在批量操作时,由于在循环内操作两个数据库的表,有两个数据库连接,Yii会在执行DB操作时切换连接,通过Schema获取表结构,非常耗时。解决方法只能是把两个表的插入操作分开,一个个批量执行来提高效率了。
    感谢老哥,结贴!

  • 回答于 2018-10-29 18:21 举报

    你这个问题 我今天刚刚遇到, where 先把大的条件放前面

    1 条回复
    回复于 2018-10-30 16:57 回复

    其实是跨库的问题,写了篇文章记录了本次的坑:https://www.jianshu.com/p/e1f5364aa9d6

您需要登录后才可以回答。登录 | 立即注册
Coder1024
职场新人

Coder1024

注册时间:2018-10-24
最后登录:2019-07-17
在线时长:2小时3分
  • 粉丝0
  • 金钱45
  • 威望0
  • 积分65

热门问题