整个2015年从开始到结尾都在进行网站向云服务器的搬迁以及网站的升级,其中大数据量的数据迁移是个令人很头痛的问题,几百万的数据量加上几十个字段,系列网站还有几十个这样的网站,需要等待数据迁移程序运行的时间真是太长太长了。上半年就遇到大数据量的问题,后来通过修改服务器配置,让PHP使用更多的内存、最大执行时间、数据库连接缓存等办法,还是用drush content-migrate-fields这样的命令来进行,算是解决了部分难以迁移的站点。但现在到年尾,而且随着Drupal 8的退出,Drupal 6很快就面临失去支持的境况,我们需要把所有Drupal 6网站都升级,现在把所有服务器资源都利用起来,还专门购买16核64G内存的云服务器临时实例来加快迁移程序的运行,但按目前的做法算下来,依然是不知道还需要几个月才能完成。😢
最早写博文《大数据量Drupal_6网站升级到Drupal_7很麻烦》的时候,采用了数据库命令来升级的办法,这无疑是最快的,但因为其它系列站数据结构复杂、数据导入及删除等情况也复杂,所以没有敢这样做。再后来一想,drush命令其实很好,但却N个字段逐个字段进行迁移,这样用的时间就是N多倍了,也尝试过想修改drush命令中涉及到的程序来加快进度,但那个程序还比较复杂,看了半天也没有看懂,不知道如何下手修改。正好前几个在搬迁一个数据量不大的英文站时,需要编写一个PHP程序来进行一些数据的匹配、修改、保存,就想到干脆自己来编写程序进行Drupal 6到Drupal 7的数据迁移,经过几天的编写、调试和使用,证明这是可行的,本来准备一个程序直接读取Drupal 6站点数据、马上写入Drupal 7站点,但调用API初始化上有些问题,就用了两个PHP程序,中间使用csv文件来做过渡:
- drupal6-file.php :按内容类型读取Drupal 6网站中的node数据,保存到csv文件中
- file-drupal7.php :逐行读取csv文件,保存到Drupal 7网站对应的内容类型中(这一步其实也可以用feeds模块来进行导入)
drupal6-file.php示范程序源代码如下:
<?php ini_set("display_errors", "On"); error_reporting(E_ALL | E_STRICT); $_SERVER['HTTP_HOST'] = 'chn.youbianku.com'; $_SERVER['REQUEST_URI'] = '/'; $_SERVER['REMOTE_ADDR'] = '127.0.0.1'; $_SERVER['SCRIPT_NAME'] = '/drupal6-file.php'; $drupal6_path = '/alidata/www/chn.youbianku.com/'; chdir($drupal6_path); require_once './includes/bootstrap.inc'; drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL); if (isset($argv[1])) { $file_name = $argv[1]; } else { $file_name = "/root/my_array.csv"; } if (isset($argv[2])) { $offset = $argv[2]; } else { $offset = 0; } if (isset($argv[3])) { $limit = $argv[3]; } else { $limit = 10; } $node_type = "address"; $type = $node_type; print "file_name = $file_name offset = $offset limit = $limit\n"; $fields = content_types($type); //print "fields['fields'] = ".$fields['fields']." \n"; $count=0; //print_r($fields); $fields_array=array(); foreach ( $fields['fields'] as $key => $field ) { $fields_array[]=$key; } //print_r($fields_array); $file = fopen($file_name,"w"); $head_array = $fields_array; array_unshift($head_array,'title'); array_unshift($head_array,'nid'); fputcsv($file,$head_array); print "$count,".implode(",",$head_array)."\n"; //$sql = "SELECT node.title, node.type, node.nid FROM {node} WHERE node.type = '$node_type' LIMIT $list_no OFFSET $offset"; $sql = "SELECT node.nid FROM {node} WHERE node.type = '$node_type' LIMIT $limit OFFSET $offset"; $result = db_query($sql); while ($anode = db_fetch_object($result)) { //print_r ($anode); $nid = $anode->nid; $node=node_load($nid); $node_array = get_object_vars($node); //print_r ($node_array); $line_array=array(); foreach ($fields_array as $field) { $line_array[$field] = $node_array[$field][0]['value']; } array_unshift($line_array,$node->title); array_unshift($line_array,$nid); $count++; print "$count,".implode(",",$line_array)."\n"; fputcsv($file,$line_array); unset($nid,$node,$node_array,$line_array,$field); } fclose($file); print "total count = $count\n"; print "-- end --\n"; ?>
file-drupal7.php示范程序源代码如下:
<?php ini_set("display_errors", "On"); error_reporting(E_ALL | E_STRICT); $_SERVER['HTTP_HOST'] = 'chn.youbianku.com'; $_SERVER['REMOTE_ADDR'] = '127.0.0.1'; $_SERVER['SCRIPT_NAME'] = '/file-drupal7.php'; $drupal7_path = '/alidata/www/chn.youbianku.com/'; chdir($drupal7_path); define('DRUPAL_ROOT', $drupal7_path); require_once './includes/bootstrap.inc'; drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL); if (isset($argv[1])) { $file_name = $argv[1]; } else { print "please provide file_name"; exit; } $file = fopen($file_name,"r"); $count=0; //$line = split(',',$fields_array); //print "line = $line\n"; //array_unshift($fields_array,'title'); //array_unshift($fields_array,'nid'); $fields_array=fgetcsv($file); print "$count,".implode(",",$fields_array)."\n"; array_shift($fields_array); array_shift($fields_array); while(! feof($file)) { $line_array = fgetcsv($file); if ($line_array == FALSE) break; $count++; print "$count,".implode(",",$line_array)."\n"; $nid = array_shift($line_array); $title = array_shift($line_array); $node=node_load($nid); // print_r($node); $node_array = get_object_vars($node); //print_r($node_array); foreach($line_array as $key => $field_value) { $field_name = $fields_array[$key]; $node_array[$field_name]['und'][0]['value'] = $field_value; } $node_new = (object) $node_array; node_save($node_new); unset($nid,$title,$node,$node_array,$line_array,$key,$field_value,$field_name,$node_new); } fclose($file); print "total count = $count\n"; print "-- end --\n"; ?>
对于超大数据量的网站来说,运行以上drupal6-file.php程序也需要很长时间,而且占用内存非常大(几十万的数量可能超过10G),可以用一个shell批处理调用php程序,分段来处理。而file-drupal7.php占用内存很少,不过依然需要很长时间运行,主要是等待RDS数据库服务器的IO。
补充:以上程序经过好些天的运行,证明是可以迁移数百万数量级页面的网站,不过对于一些drupal 6中内容为空的字段,用drush content-migrate-fields迁移是不在drupal 7中的字段产生新的纪录,而用上面的程序还是会产生内容为空格('')的记录,显得不必要和占用空间,可以用下面类似的语句来判断和删除:
DELETE FROM `postcodebase_tx`.`field_data_field_urbcitystate` WHERE `field_urbcitystate_value` = ''; DELETE FROM `postcodebase_tx`.`field_data_field_urbcitystate` WHERE `field_urbcitystate_value` is null;
2016年2月23日补充:上面是从drupal 6读取字段保存到csv文件,然后再读取csv文件保存到drupal 7的字段,中间用了一个csv文件作为中转。这几天还在做一些其它网站的数据迁移收尾工作,遇到分类字段,就尝试了不用csv文件过渡,而是在drupal 7中直接读取数据库中drupal 6老版本数据表中的字段内容,保存到对应的drupal 7的字段中,这样更简单、快捷,程序例子:
<?php ini_set("display_errors", "On"); error_reporting(E_ALL | E_STRICT); $_SERVER['HTTP_HOST'] = 'trade.mingluji.com'; $_SERVER['REMOTE_ADDR'] = '127.0.0.1'; $_SERVER['SCRIPT_NAME'] = '/trade.php'; $drupal7_path = '/alidata/www/drupal_update.mingluji.com/'; chdir($drupal7_path); define('DRUPAL_ROOT', $drupal7_path); require_once './includes/bootstrap.inc'; drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL); $count=0; $count_change=0; $count_not_change=0; $count_null=0; if (isset($argv[1])) { $node_type = $argv[1]; } else { print "please provide node_type"; exit; } if (isset($argv[2])) { $offset = $argv[2]; } else { $offset = 0; } if (isset($argv[3])) { $limit = $argv[3]; } else { $limit = 100000; } //$node_type = "gongying"; print "node_type = $node_type, offset = $offset, limit = $limit\n"; $sql = "SELECT node.nid FROM {node} WHERE node.type = '$node_type' LIMIT $limit OFFSET $offset"; $result = db_query($sql); while ($anode = $result->fetch()) { $count++; $node=node_load($anode->nid); //$node=node_load('51254'); $nid=$node->nid; if ($nid == NULL) { print "nid is null\n"; $count_null++; continue; } $sql1 = "SELECT field_area_value FROM content_field_area WHERE nid=$nid"; $result1 = db_query($sql1); $array1 = $result1->fetch(); //print_r($array1); $tid = $array1->field_area_value; $new=$node; if ($tid != NULL && $tid != $new->field_area['und'][0]['tid']) { //print_r($new); $new->field_area['und'][0]['tid']=$tid; node_save($new); $count_change++; } else { $count_not_change++; } print "count=$count, nid=$nid, tid=$tid\n"; } echo "Done!\n"; print "count=$count, count_change=$count_change, count_not_change=$count_not_change, count_null = $count_null\n"; ?>
上面程序已经在需要的网站中运行成功,如果需要修改字段名称或者是多值字段的话,可以另存为多个程序来执行。
评论