Movable Type 的数据库瓶颈

<p>随笔 而已,能力所限,只是个人看法。</p>

<p>就向大家看见的这样,由于多说服务器的原因,路杨正在逐步把 多说的评论写回本地 Movable Type 的数据库。</p>

<p>过程不算难。通过 API 获得 JSON 数据,分析后写入MT评论数据库罢了。
为了区分和拓展,给 MT 数据库的评论表增加了3行,分别记录 远程服务器名(remote_service),远程服务器ID (remote_id) 和 UA (
agent)。</p>

<p>开始直接 Perl 脚本。核心代码为</p>

   sub check_ds_comments {
       my $list_ref;
       my $log_id;
       foreach my $a(@{$response} ) {
        my $act = $a->{'action'}; 
        if ( $act eq 'create') {   my $ds = $a-&gt;{'meta'};  my  $ds_id= $ds-&gt;{"post_id"};    $list_ref->{$ds_id} = $ds;  } 
       else
       {  
               foreach my $var ( @{$a->{'meta'}} ) { $list_ref-&gt;{$var}->{'status'} = ($act eq 'approve') ? 'approved' : $act ;  }
        }
         $log_id = $a->{'log_id'};  
       }

    my $str;
    my $nos = 0;        
    my @ids =sort {$a &lt;=&gt; $b} keys %$list_ref;        
    foreach my $ids ( @ids )
    {
       # print "<br>$ids:n<br>";
       my $dscs = $list_ref->{$ids};
      print "<br>t$ids =&gt; $dscs->{'status'} n<br>";
      next if ( ($dscs-&gt;{'status'} eq 'delete-forever') || ($dscs->{'status'} eq 'delete') || ($dscs->{'status'} eq 'spam'));
      next if  ($dscs-&gt;{"thread_key"}  !~ /^\d+$/);
      &import_comments($dscs);
      $nos ++;
   }                   
    $str .= "<p>信息:</p>"  ;
    $str .= "&lt;br&gt;last_log_id=" .$log_id;        
    $str .= "&lt;br&gt;共有有效评论 " .$nos ." 条。OK!!!!<br>";       
    print $str; 

} 

sub import_comments {
  my $ds = shift ;
  print "<ul><li>--正常评论,开始处理----------------------------</li>";
  print "<li>信息如下:<br />";
  print "thread_key =". $ds->{"thread_key"}. "<br />";
  print "post_id =". $ds->{"post_id"}. "<br />";
  print "author_name =". $ds->{"author_name"}. "<br />";
  print "message =". $ds->{"message"};
  print "</li>";

 print "<li>检查 id=". $ds->{"post_id"} ." 的多说评论是否存在于本地数据库</li>" ;
  my $comment_id = &amp;get_id_form_ds_id( $ds->{"post_id"},'no_output'); # comment_parent_id# ,
  if ( $comment_id ne 'NULL' )
  { 
   print "<li>这条多说评论已经存在。 本地数据库中 id=". $comment_id ."</li>" ;
   print "</ul>" ;
   return; 
  }
  else {  print "<li>没有在本地数据库中检查到这条评论,准备写入本地数据库..........</li>"; }


my $blog_id = 2;
my $mt_id =  ($ds->{'author_id'} =='11415448' )?  1 : 'NULL';
&c_to_c($ds->{"message"});
&c_to_c($ds->{"agent"});

  my $str ='INSERT INTO mt_comment (comment_id, comment_author, comment_blog_id, comment_commenter_id, comment_created_by, comment_created_on, comment_email, comment_entry_id, comment_ip, comment_junk_log, comment_junk_score, comment_junk_status, comment_last_moved_on, comment_modified_by, comment_modified_on, comment_parent_id, comment_text, comment_url, comment_visible, comment_remote_id, comment_remote_service, comment_agent) VALUES
(';

$str .=$comment_id .',';# comment_id# ,
$str .='"'. $ds->{"author_name"} .'",'; # comment_author# ,
$str .='2,' ;  # comment_blog_id# ,
$str .= $mt_id .','; #$ds->{"mt_author_id"}.',';  # comment_commenter_id# ,
$str .='NULL,';  # comment_created_by# ,
$str .='"'. $ds->{"created_at"} .'",'; # comment_created_on# ,
$str .='"'. $ds->{"author_email"} .'",';  # comment_email# ,
$str .=int($ds->{"thread_key"}) .','; # comment_entry_id# ,
$str .='"'. $ds->{"ip"} .'",'; # comment_ip# ,
$str .='NULL,';  # comment_junk_log# ,
$str .='NULL,'; # comment_junk_score# ,
$str .='1,';  # comment_junk_status# ,
$str .="'2000-01-01 00:00:00',"; # comment_last_moved_on# ,
$str .='NULL,'; # comment_modified_by# ,
$str .='NULL,'; # comment_modified_on# ,
$str .= &amp;get_id_form_ds_id( $ds->{"parent_id"} ).','; # comment_parent_id# ,
$str .="'". $ds->{"message"} ."',";  # comment_text# ,
$str .="'". $ds->{"author_url"} ."',";  # comment_url# ,
$str .='0,';  # comment_visible# ,   # 0 代表 等待发布。 1代表直接发布
$str .=$ds->{"post_id"}.','; # comment_remote_id# ,
$str .='"DUOSHUO",' ;  # comment_remote_service# \
$str .='"'. $ds->{"agent"}. '"'; # comment_created_on# ,
$str .=');' ;
 print "<li>";
 print "str: n<br />";
 print $str." n</li>";
my $rows = $dbh->do($str) or die "Can't execute sql: ".$dbh->errstr."n";
print "</ul>" ;


}
# 转码符号
sub c_to_c
{
my $str = shift ;
$$str =~ s/'/\'/gs;
$$str =~ s/"/\"/gs;
$$str =~ s/,/\,/gs;
$$str =~ s/;/\;/gs;

}
# 检查是否存在此条评论
sub get_id_form_ds_id
{
 my $dsparent_id = shift ;
 my $no_output = shift ;
 unless ($no_output) { print "&lt;br /&gt;dsparent_id -&gt; comment_id \n&lt;br /&gt;dsparent_id = ". $dsparent_id ."n<br />"; }
  my $delim = 'NULL';     

  my $sql= "select comment_id from mt_comment where comment_remote_id ='". $dsparent_id ."'";

  my $sth = $dbh->prepare($sql);   #准备  
  $sth-&gt;execute() or die "Cannot execute: " . $sth->errstr();  #执行
  while (my @result = $sth->fetchrow_array) {
      $delim = @result[0];        

 }

  $sth->finish;#结束句柄
  unless ($no_output) { print '&lt;br&gt;检查到id=' . $delim. "n<br />"; }
  $delim;
}

<p>代码很随意,设置读取 200 条评论, 几乎瞬间写入 MySQL 数据库。
界面代码为: http://mt.easun.org/cgi-bin/Util/ds.cgi</p>

<p>登录 MT 后台,查看评论,发现所有的多说远程评论都乖乖的显示在了本地评论列表里面。</p>

<p>本来一切都 OK 了。 但是突想:既为什么不写成 MT 的插件呢, 这样可以利用 MT 自身封装的函数操作数据库,也不用硬编码进 MySQL 的用户名和密码。 说干就干。 其实有了 Perl脚本,改起来也很容易:
核心代码改写如下:</p>

 sub check_ds_comments {
    my $app = shift ;     
    my $response = shift ;     
    my $list_ref;
    my $log_id;
    foreach my $a(@{$response} ) {
        my $act = $a->{'action'}; 
        if ( $act eq 'create') {   my $ds = $a-&gt;{'meta'};  my  $ds_id= $ds-&gt;{"post_id"};    $list_ref->{$ds_id} = $ds;  } 
       else
       {  
               foreach my $var ( @{$a->{'meta'}} ) { $list_ref-&gt;{$var}->{'status'} = ($act eq 'approve') ? 'approved' : $act ;  }
        }
         $log_id = $a->{'log_id'};  
    }

    my $str;
    my $nos = 0;        
    my @ids =sort {$a &lt;=&gt; $b} keys %$list_ref;        
    foreach my $ids ( @ids )
    {
       # print "<br>$ids:n<br>";
       my $dscs = $list_ref->{$ids};
      $str .= "&lt;br&gt;\t$ids => $dscs->{'status'} n<br>";
      next if ( ($dscs-&gt;{'status'} eq 'delete-forever') || ($dscs->{'status'} eq 'delete') || ($dscs->{'status'} eq 'spam'));
      next if  ($dscs-&gt;{"thread_key"}  !~ /^\d+$/);
      &import_comments($dscs);
      $nos ++;
   }                   
    $str .= "<p>信息:</p>"  ;
    $str .= "&lt;br&gt;last_log_id=" .$log_id;        
    $str .= "&lt;br&gt;共有有效评论 " .$nos ." 条。OK!!!!<br>";       
    return $app-&gt;error($str); 

 }
sub import_comments {
    my $ds = shift ;
    require MT::Comment;
    my @comments = MT::Comment->load( 
    { remote_service =>'DUOSHUO', remote_id=>$ds->{"post_id"},}, 
    { sort => 'created_on', direction => 'ascend',}  
    );
  my $comment =  @comments[0] ||  MT::Comment->new;

  my $str ; 
  if ( $comment->id )
  {
    $str=  "&lt;li&gt;这个多说评论已经存在。 数据库中 id=". $comment->id ."</li>" ;      
    return; 
  }
  else {  $str .= "<li>没有检查到这个评论,准备写入...........</li>"; }


    $comment-&gt;author($ds->{"author_name"} );# comment_  author# ,
    $comment->blog_id(2); # comment_  blog_id# ,
    $comment-&gt;commenter_id(1)  if ($ds->{'author_id'} =='11415448' ) ; # comment_  commenter_id# ,
    $comment-&gt;email($ds->{"author_email"} );  # comment_  email# ,
    $comment-&gt;entry_id( int($ds->{"thread_key"}) ); # comment_  entry_id# ,
    $comment-&gt;ip( $ds->{"ip"} ); # comment_  ip# ,
    $comment-&gt;parent_id( &amp;get_id_form_ds_id( $ds->{"parent_id"} ) ); # comment_  parent_id# ,
    $comment-&gt;text( $ds->{"message"} );  # comment_  text# ,
    $comment-&gt;url( $ds->{"author_url"} );  # comment_  url# ,
    $comment-&gt;visible(0) unless ($comment->id) ;  # comment_  visible# ,   # 0 代表 等待发布。 1代表直接发布
    $comment-&gt;remote_id($ds->{"post_id"}); # comment_  remote_id# ,
    $comment->remote_service('DUOSHUO' );  # comment_  remote_service# \
    $comment-&gt;agent($ds->{"agent"}) ; # comment_  agent# ,
    $comment-&gt;save  or die $comment->errstr;
}

<p>代码的确简洁多了~~~~</p>

<p>在后台启用此插件。运行。。。、
浏览器漫长的等待,结果返回 504 。。。。。</p>

<p>My GOD .....
修改获取多说数据为 20 条,这次正常了。但是也几乎耗时5分钟。。。</p>

<p>MT 的代码太复杂了,几乎可以判断资源浪费在了 $comment->save 上,但是为什么不得而知。。</p>

<p>按理来说,同样是Perl脚本,同样是连接 MySQL 。为什么差别这么大呢? Movable Type 看来真的该减肥了。。</p>

<p>懒惰了。 就这样使用独立的 Perl 脚本了。</p>

<p>这次算是把 多说 的所有评论都反向同步到了本地,同时 将 http://mt.easun.org/cgi-bin/Util/ds.cgi 作为了多说系统的本地回调地址,这样,每次评论,都可以即时写进本地数据库。</p>

<p>PS: 顺手改了一下多说的 JS ,让它不重复显示已经同步并发表出来的多说评论, 仅仅显示没有发布出来的多说评论。</p>

添加新评论