eee
This commit is contained in:
+273
-277
@@ -18,286 +18,282 @@ use Symfony\Component\Console\Output\OutputInterface;
|
|||||||
*/
|
*/
|
||||||
class BackupCommand extends Command
|
class BackupCommand extends Command
|
||||||
{
|
{
|
||||||
|
|
||||||
public string $command = 'db:backup';
|
|
||||||
|
|
||||||
|
|
||||||
public string $description = 'php kiri.php db:backup --database users --table u_user --data 1 /Users/admin/snowflake-bi/test.sql';
|
|
||||||
|
|
||||||
|
|
||||||
private LocalService $service;
|
|
||||||
|
|
||||||
public array $percentStatus = [];
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
protected function configure()
|
|
||||||
{
|
|
||||||
$this->service = \Kiri::getDi()->get(LocalService::class);
|
|
||||||
$this->setName('db:backup')
|
|
||||||
->addOption('data', 'd', InputArgument::OPTIONAL)
|
|
||||||
->addArgument('path', InputArgument::REQUIRED, "save to path", null)
|
|
||||||
->addOption('table', 't', InputArgument::OPTIONAL)
|
|
||||||
->addOption('database', 'db', InputArgument::OPTIONAL)
|
|
||||||
->setDescription('php kiri.php db:backup --database users --table u_user --data 1 /Users/admin/snowflake-bi/test.sql');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param InputInterface $input
|
|
||||||
* @param OutputInterface $output
|
|
||||||
* @return int
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
public function execute(InputInterface $input, OutputInterface $output): int
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
/** @var Connection $data */
|
|
||||||
$data = $this->service->get($input->getOption('database'));
|
|
||||||
|
|
||||||
$table = $input->getOption('table');
|
|
||||||
if ($table !== null) {
|
|
||||||
$table = explode(',', $table);
|
|
||||||
} else {
|
|
||||||
$tmp = $data->createCommand('show tables from `' . $data->database . '`')->all();
|
|
||||||
$table = [];
|
|
||||||
foreach ($tmp as $value) {
|
|
||||||
$table[] = current($value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$path = $input->getArgument('path');
|
public string $command = 'db:backup';
|
||||||
if (!str_starts_with($path, '/')) {
|
|
||||||
$path = APP_PATH . $path;
|
|
||||||
}
|
|
||||||
|
|
||||||
$isData = $input->getOption('data');
|
|
||||||
if (!is_dir($path)) {
|
|
||||||
mkdir($path);
|
|
||||||
}
|
|
||||||
|
|
||||||
$waite = new Coroutine\WaitGroup();
|
|
||||||
foreach ($table as $value) {
|
|
||||||
$tableInfo = $data->createCommand('show create table `' . $data->database . '`.`' . $value . '`')->one();
|
|
||||||
|
|
||||||
$tmp = rtrim($path, '/') . '/' . current($tableInfo) . '.sql';
|
|
||||||
if (!file_exists($tmp)) {
|
|
||||||
touch($tmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
file_put_contents($tmp, '');
|
|
||||||
|
|
||||||
$tableCreator = next($tableInfo);
|
|
||||||
if (preg_match('/AUTO_INCREMENT=\d+\s/', $tableCreator)) {
|
public string $description = 'php kiri.php db:backup --database users --table u_user --data 1 /Users/admin/snowflake-bi/test.sql';
|
||||||
$tableCreator = preg_replace('/AUTO_INCREMENT=\d+\s/', 'AUTO_INCREMENT=1 ', $tableCreator);
|
|
||||||
}
|
|
||||||
|
private LocalService $service;
|
||||||
file_put_contents($tmp, $tableCreator . ';' . PHP_EOL . PHP_EOL, FILE_APPEND);
|
|
||||||
|
public array $percentStatus = [];
|
||||||
if ($isData == 1) {
|
|
||||||
$waite->add(1);
|
|
||||||
Coroutine::create(function () use ($waite, $input, $value, $tmp) {
|
/**
|
||||||
defer(function () use ($waite) {
|
*
|
||||||
$waite->done();
|
*/
|
||||||
});
|
protected function configure()
|
||||||
$this->writeData( $input->getOption('database'), $value, $tmp);
|
{
|
||||||
});
|
$this->service = \Kiri::getDi()->get(LocalService::class);
|
||||||
}
|
$this->setName('db:backup')
|
||||||
}
|
->addOption('data', 'd', InputArgument::OPTIONAL)
|
||||||
|
->addArgument('path', InputArgument::REQUIRED, "save to path", null)
|
||||||
$waite->wait();
|
->addOption('table', 't', InputArgument::OPTIONAL)
|
||||||
|
->addOption('database', 'db', InputArgument::OPTIONAL)
|
||||||
$output->write('dump data success');
|
->setDescription('php kiri.php db:backup --database users --table u_user --data 1 /Users/admin/snowflake-bi/test.sql');
|
||||||
} catch (\Throwable $throwable) {
|
}
|
||||||
$output->writeln(throwable($throwable));
|
|
||||||
} finally {
|
|
||||||
return 1;
|
/**
|
||||||
}
|
* @param InputInterface $input
|
||||||
}
|
* @param OutputInterface $output
|
||||||
|
* @return int
|
||||||
|
* @throws Exception
|
||||||
public bool $isEnd = false;
|
*/
|
||||||
|
public function execute(InputInterface $input, OutputInterface $output): int
|
||||||
|
{
|
||||||
/**
|
try {
|
||||||
* @param string $dbname
|
/** @var Connection $data */
|
||||||
* @param string $tableName
|
$data = $this->service->get($input->getOption('database'));
|
||||||
* @param string $path
|
|
||||||
* @return void
|
$table = $input->getOption('table');
|
||||||
* @throws Exception
|
if ($table !== null) {
|
||||||
*/
|
$table = explode(',', $table);
|
||||||
public function writeData(string $dbname, string $tableName, string $path): void
|
} else {
|
||||||
{
|
$tmp = $data->createCommand('show tables from `' . $data->database . '`')->all();
|
||||||
$offset = 0;
|
$table = [];
|
||||||
$size = 1000;
|
foreach ($tmp as $value) {
|
||||||
|
$table[] = current($value);
|
||||||
$channel = new Channel(200);
|
}
|
||||||
for ($i = 0; $i < 200; $i++) {
|
}
|
||||||
Coroutine::create(function () use ($channel, $path, $dbname, $tableName) {
|
|
||||||
while ($channel->errCode != SWOOLE_CHANNEL_CLOSED) {
|
$path = $input->getArgument('path');
|
||||||
|
if (!str_starts_with($path, '/')) {
|
||||||
|
$path = APP_PATH . $path;
|
||||||
|
}
|
||||||
|
|
||||||
|
$isData = $input->getOption('data');
|
||||||
|
if (!is_dir($path)) {
|
||||||
|
mkdir($path);
|
||||||
|
}
|
||||||
|
|
||||||
|
$waite = new Coroutine\WaitGroup();
|
||||||
|
foreach ($table as $value) {
|
||||||
|
$tableInfo = $data->createCommand('show create table `' . $data->database . '`.`' . $value . '`')->one();
|
||||||
|
|
||||||
|
$tmp = rtrim($path, '/') . '/' . current($tableInfo) . '.sql';
|
||||||
|
if (!file_exists($tmp)) {
|
||||||
|
touch($tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
file_put_contents($tmp, '');
|
||||||
|
|
||||||
|
$tableCreator = next($tableInfo);
|
||||||
|
if (preg_match('/AUTO_INCREMENT=\d+\s/', $tableCreator)) {
|
||||||
|
$tableCreator = preg_replace('/AUTO_INCREMENT=\d+\s/', 'AUTO_INCREMENT=1 ', $tableCreator);
|
||||||
|
}
|
||||||
|
|
||||||
|
file_put_contents($tmp, $tableCreator . ';' . PHP_EOL . PHP_EOL, FILE_APPEND);
|
||||||
|
|
||||||
|
if ($isData == 1) {
|
||||||
|
$waite->add(1);
|
||||||
|
Coroutine::create(function () use ($waite, $input, $value, $tmp) {
|
||||||
|
defer(function () use ($waite) {
|
||||||
|
$waite->done();
|
||||||
|
});
|
||||||
|
$this->writeData($input->getOption('database'), $value, $tmp);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$waite->wait();
|
||||||
|
|
||||||
|
$output->write('dump data success');
|
||||||
|
} catch (\Throwable $throwable) {
|
||||||
|
$output->writeln(throwable($throwable));
|
||||||
|
} finally {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public bool $isEnd = false;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $dbname
|
||||||
|
* @param string $tableName
|
||||||
|
* @param string $path
|
||||||
|
* @return void
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function writeData(string $dbname, string $tableName, string $path): void
|
||||||
|
{
|
||||||
|
$offset = 0;
|
||||||
|
$size = 1000;
|
||||||
|
|
||||||
|
$channel = new Channel(200);
|
||||||
|
for ($i = 0; $i < 200; $i++) {
|
||||||
|
Coroutine::create(function () use ($channel, $path, $dbname, $tableName) {
|
||||||
|
while ($channel->errCode != SWOOLE_CHANNEL_CLOSED) {
|
||||||
if (($data = $channel->pop()) === false) {
|
if (($data = $channel->pop()) === false) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (($value = json_decode($data, true)) === null) {
|
if (($value = json_decode($data, true)) === null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$value = $this->toSql($dbname, $tableName, $value);
|
$value = $this->toSql($dbname, $tableName, $value);
|
||||||
|
|
||||||
file_put_contents($path, $value . PHP_EOL, FILE_APPEND);
|
file_put_contents($path, $value . PHP_EOL, FILE_APPEND);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
$id = Coroutine::create(function () use ($channel) {
|
$id = Coroutine::create(function () use ($channel) {
|
||||||
$data = Coroutine::waitSignal(SIGTERM | SIGINT);
|
$data = Coroutine::waitSignal(SIGTERM | SIGINT);
|
||||||
if ($data) {
|
if ($data) {
|
||||||
$this->isEnd = true;
|
$this->isEnd = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
/** @var Connection $database */
|
/** @var Connection $database */
|
||||||
$database = \Kiri::service()->get($dbname);
|
$database = \Kiri::service()->get($dbname);
|
||||||
|
|
||||||
$total = $database->createCommand("SELECT COUNT(*) as total FROM " . $tableName)->one()['total'];
|
$total = $database->createCommand("SELECT COUNT(*) as total FROM " . $tableName)->one()['total'];
|
||||||
|
|
||||||
$startTime = time();
|
$startTime = time();
|
||||||
|
|
||||||
$wait = new Coroutine\WaitGroup();
|
$wait = new Coroutine\WaitGroup();
|
||||||
while ($this->isEnd === false && $offset < $total) {
|
while ($this->isEnd === false && $offset < $total) {
|
||||||
$wait->add(1);
|
$wait->add(1);
|
||||||
Coroutine::create(function () use ($wait, $offset, $size, $total, $dbname, $tableName, $channel) {
|
Coroutine::create(function () use ($wait, $offset, $size, $total, $dbname, $tableName, $channel) {
|
||||||
defer(function () use ($wait) {
|
defer(function () use ($wait) {
|
||||||
$wait->done();
|
$wait->done();
|
||||||
});
|
});
|
||||||
/** @var Connection $database */
|
/** @var Connection $database */
|
||||||
$database = \Kiri::service()->get($dbname);
|
$database = \Kiri::service()->get($dbname);
|
||||||
|
|
||||||
$data = $database->createCommand("SELECT * FROM $tableName LIMIT $offset,$size")->all();
|
$data = $database->createCommand("SELECT * FROM $tableName LIMIT $offset,$size")->all();
|
||||||
if (is_bool($data) || count($data) < 1) {
|
if (is_bool($data) || count($data) < 1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$channel->push(json_encode($data));
|
$channel->push(json_encode($data));
|
||||||
|
|
||||||
$this->percentStatus[$dbname . $tableName] = intval(round(($offset + $size) / $total, 2) * 100);
|
$this->percentStatus[$dbname . $tableName] = intval(round(($offset + $size) / $total, 2) * 100);
|
||||||
|
|
||||||
$this->outputProgress(true);
|
$this->outputProgress(true);
|
||||||
});
|
});
|
||||||
$offset += $size;
|
$offset += $size;
|
||||||
}
|
}
|
||||||
|
|
||||||
$wait->wait();
|
$wait->wait();
|
||||||
|
|
||||||
echo 'use time ' . (time() - $startTime) . 's' . PHP_EOL;
|
echo 'use time ' . (time() - $startTime) . 's' . PHP_EOL;
|
||||||
|
|
||||||
$channel->close();
|
$channel->close();
|
||||||
|
|
||||||
Coroutine::cancel($id);
|
Coroutine::cancel($id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $dbname
|
* @param string $dbname
|
||||||
* @param string $value
|
* @param string $value
|
||||||
* @param array $data
|
* @param array $data
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function toSql(string $dbname, string $value, array $data): string
|
public function toSql(string $dbname, string $value, array $data): string
|
||||||
{
|
{
|
||||||
$strings = ['INSERT INTO ' . $dbname . '.' . $value];
|
$strings = ['INSERT INTO ' . $dbname . '.' . $value];
|
||||||
foreach ($data as $datum) {
|
$strings[] = '(' . implode(',', array_keys($data[0])) . ') VALUES';
|
||||||
if (count($strings) == 1) {
|
foreach ($data as $datum) {
|
||||||
$keys = array_keys($datum);
|
$keys = array_values($datum);
|
||||||
$strings[] = '(' . implode(',', $keys) . ') VALUES';
|
$encode = [];
|
||||||
} else {
|
foreach ($keys as $val) {
|
||||||
$keys = array_values($datum);
|
if (is_string($val)) {
|
||||||
$encode = [];
|
$encode[] = '\'' . addcslashes($val, '\'') . '\'';
|
||||||
foreach ($keys as $val) {
|
} else {
|
||||||
if (is_string($val)) {
|
$encode[] = $val === '' ? '\'\'' : $val;
|
||||||
$encode[] = '\'' . $val . '\'';
|
}
|
||||||
} else {
|
}
|
||||||
$encode[] = $val === '' ? '\'\'' : $val;
|
$strings[] = '(' . implode(',', $encode) . '),';
|
||||||
}
|
}
|
||||||
}
|
return rtrim(implode('', $strings), ',') . ';';
|
||||||
$strings[] = '(' . implode(',', $encode) . '),';
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
return rtrim(implode('', $strings), ',') . ';';
|
/**
|
||||||
}
|
* @param $key
|
||||||
|
* @param $percent
|
||||||
|
* @return string
|
||||||
/**
|
* 组合成进度条
|
||||||
* @param $key
|
*/
|
||||||
* @param $percent
|
public function buildLine($key, $percent): string
|
||||||
* @return string
|
{
|
||||||
* 组合成进度条
|
$repeatTimes = 100;
|
||||||
*/
|
if ($percent > 100) {
|
||||||
public function buildLine($key, $percent): string
|
$percent = 100;
|
||||||
{
|
}
|
||||||
$repeatTimes = 100;
|
if ($percent > 0) {
|
||||||
if ($percent > 100) {
|
$hasColor = str_repeat('■', $percent);
|
||||||
$percent = 100;
|
} else {
|
||||||
}
|
$hasColor = '';
|
||||||
if ($percent > 0) {
|
}
|
||||||
$hasColor = str_repeat('■', $percent);
|
|
||||||
} else {
|
if ($repeatTimes - $percent > 0) {
|
||||||
$hasColor = '';
|
$noColor = str_repeat(' ', $repeatTimes - $percent);
|
||||||
}
|
} else {
|
||||||
|
$noColor = '';
|
||||||
if ($repeatTimes - $percent > 0) {
|
}
|
||||||
$noColor = str_repeat(' ', $repeatTimes - $percent);
|
|
||||||
} else {
|
$buffer = "[{$hasColor}{$noColor}]";
|
||||||
$noColor = '';
|
if ($percent !== 100) {
|
||||||
}
|
$percentString = sprintf("[ %s %-6s]", $key, $percent . '%');
|
||||||
|
} else {
|
||||||
$buffer = "[{$hasColor}{$noColor}]";
|
$percentString = sprintf("[ %s %-5s]", $key, 'OK');;
|
||||||
if ($percent !== 100) {
|
}
|
||||||
$percentString = sprintf("[ %s %-6s]", $key, $percent . '%');
|
|
||||||
} else {
|
return $percentString . $buffer . "\r";
|
||||||
$percentString = sprintf("[ %s %-5s]", $key, 'OK');;
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
return $percentString . $buffer . "\r";
|
* @param bool $clear
|
||||||
}
|
* @return void
|
||||||
|
* 输出进度条
|
||||||
/**
|
*/
|
||||||
* @param bool $clear
|
public function outputProgress(bool $clear = false): void
|
||||||
* @return void
|
{
|
||||||
* 输出进度条
|
if ($clear) {
|
||||||
*/
|
$number = count($this->percentStatus);
|
||||||
public function outputProgress(bool $clear = false): void
|
for ($i = 0; $i < $number; $i++) {
|
||||||
{
|
system("tput cuu1");
|
||||||
if ($clear) {
|
system("tput el");
|
||||||
$number = count($this->percentStatus);
|
}
|
||||||
for ($i = 0; $i < $number; $i++) {
|
}
|
||||||
system("tput cuu1");
|
foreach ($this->percentStatus as $key => $value) {
|
||||||
system("tput el");
|
echo $this->buildLine($key, $value) . "\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
foreach ($this->percentStatus as $key => $value) {
|
|
||||||
echo $this->buildLine($key, $value) . "\n";
|
/**
|
||||||
}
|
* @param $k
|
||||||
}
|
* @param $value
|
||||||
|
* @return void
|
||||||
/**
|
* 更新进度条值
|
||||||
* @param $k
|
*/
|
||||||
* @param $value
|
public function updateProgressValue($k, $value): void
|
||||||
* @return void
|
{
|
||||||
* 更新进度条值
|
$this->percentStatus[$k] = $value;
|
||||||
*/
|
if ($this->percentStatus[$k] >= 1000) {
|
||||||
public function updateProgressValue($k, $value): void
|
$this->percentStatus[$k] = 1000;
|
||||||
{
|
$this->outputProgress(true);
|
||||||
$this->percentStatus[$k] = $value;
|
return;
|
||||||
if ($this->percentStatus[$k] >= 1000) {
|
}
|
||||||
$this->percentStatus[$k] = 1000;
|
|
||||||
$this->outputProgress(true);
|
$this->outputProgress(true);
|
||||||
return;
|
usleep(50000);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->outputProgress(true);
|
|
||||||
usleep(50000);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user