您在這裡

自己編程進行Drupal 6網站升級數據遷移到Drupal 7

James Qi 在 2015年12月24日 - 15:32 發表

  整個2015年從開始到結尾都在進行網站向雲服務器的搬遷以及網站的升級,其中大數據量的數據遷移是個令人很頭痛的問題,幾百萬的數據量加上幾十個字段,系列網站還有幾十個這樣的網站,需要等待數據遷移程序運行的時間真是太長太長了。上半年就遇到大數據量的問題,後來通過修改服務器配置,讓PHP使用更多的内存、最大執行時間、數據庫連接緩存等辦法,還是用drush content-migrate-fields這樣的命令來進行,算是解決了部分難以遷移的站點。但現在到年尾,而且随着Drupal 8的退出,Drupal 6很快就面臨失去支持的境況,我們需要把所有Drupal 6網站都升級,現在把所有服務器資源都利用起來,還專門購買16核64G内存的雲服務器臨時實例來加快遷移程序的運行,但按目前的做法算下來,依然是不知道還需要幾個月才能完成。sad

  最早寫博文《大數據量Drupal_6網站升級到Drupal_7很麻煩》的時候,采用了數據庫命令來升級的辦法,這無疑是最快的,但因為其它系列站數據結構複雜、數據導入及删除等情況也複雜,所以沒有敢這樣做。再後來一想,drush命令其實很好,但卻N個字段逐個字段進行遷移,這樣用的時間就是N多倍了,也嘗試過想修改drush命令中涉及到的程序來加快進度,但那個程序還比較複雜,看了半天也沒有看懂,不知道如何下手修改。正好前幾個在搬遷一個數據量不大的英文站時,需要編寫一個PHP程序來進行一些數據的匹配、修改、保存,就想到幹脆自己來編寫程序進行Drupal 6到Drupal 7的數據遷移,經過幾天的編寫、調試和使用,證明這是可行的,本來準備一個程序直接讀取Drupal 6站點數據、馬上寫入Drupal 7站點,但調用API初始化上有些問題,就用了兩個PHP程序,中間使用csv文件來做過渡:

  1. drupal6-file.php :按内容類型讀取Drupal 6網站中的node數據,保存到csv文件中
  2. 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";
?>

  上面程序已經在需要的網站中運行成功,如果需要修改字段名稱或者是多值字段的話,可以另存為多個程序來執行。

發表新回應

Plain text

  • 不允許使用 HTML 標籤。
  • 自動將網址與電子郵件地址轉變為連結。
  • 自動斷行和分段。