Запись на существующие файлы в базе данных файловой системы - PullRequest
1 голос
/ 09 мая 2019

У меня есть функция, которая записывает ~ 120Kb-150Kb HTML и метаданные в ~ 8000 .md файлов с фиксированными именами каждые несколько минут:

a-agilent-technologies-healthcare-nyse-us-39d4
aa-alcoa-basic-materials-nyse-us-159a
aaau-perth-mint-physical-gold--nyse-us-8ed9
aaba-altaba-financial-services-nasdaq-us-26f5
aac-healthcare-nyse-us-e92a
aadr-advisorshares-dorsey-wright-adr--nyse-us-d842
aal-airlines-industrials-nasdaq-us-29eb
  • Если файл не существует, онгенерирует / пишет довольно быстро.
  • Если, однако, файл существует, он делает то же самое намного медленнее, поскольку существующий файл несет данные ~ 150 КБ.

Как мне решить эту проблему?

Сгенерировать ли я новый файл с новым именем в том же каталоге и отменить связь старого файла в forцикл?

или я создаю новую папку и записываю все файлы, а затем отменяю связь с предыдущим каталогом?Проблема этого метода заключается в том, что иногда 90% файлов перезаписываются, а некоторые остаются прежними.


Код

Эта функция вызывается в цикле for, который вы можете увидеть в этой ссылке

public static function writeFinalStringOnDatabase($equity_symbol, $md_file_content, $no_extension_filename)
{
    /**
     *@var is the MD file content with meta and entire HTML
     */
    $md_file_content = $md_file_content . ConfigConstants::NEW_LINE . ConfigConstants::NEW_LINE;
    $dir = __DIR__ . ConfigConstants::DIR_FRONT_SYMBOLS_MD_FILES; // symbols front directory
    $new_filename = EQ::generateFileNameFromLeadingURL($no_extension_filename, $dir);

    if (file_exists($new_filename)) {
        if (is_writable($new_filename)) {
            file_put_contents($new_filename, $md_file_content);
            if (EQ::isLocalServer()) {
                echo $equity_symbol . " ? " . ConfigConstants::NEW_LINE;
            }

        } else {
            if (EQ::isLocalServer()) {
                echo $equity_symbol . " symbol MD file is not writable in " . __METHOD__ . " ? Maybe, check permissions!" . ConfigConstants::NEW_LINE;
            }
        }
    } else {
        $fh = fopen($new_filename, 'wb');
        fwrite($fh, $md_file_content);
        fclose($fh);
        if (EQ::isLocalServer()) {
            echo $equity_symbol . " front md file does not exit in " . __METHOD__ . " It's writing on the database now ?" . ConfigConstants::NEW_LINE;
        }

    }

}

1 Ответ

1 голос
/ 22 мая 2019

Я давно не программировал на PHP, но этот вопрос сегодня заинтересовал меня. : D

Предложение

Как мне решить эту проблему? Сгенерировать ли я новый файл с новым именем в том же каталоге и отменить связь старого файла в цикле for?

Просто используйте 3 amigos fopen(), fwrite() & fclose() снова, поскольку fwrite также перезапишет все содержимое существующего файла.

if (file_exists($new_filename)) {
    if (is_writable($new_filename)) {
        $fh = fopen($new_filename,'wb');
        fwrite($fh, $md_file_content);
        fclose($fh);

        if (EQ::isLocalServer()) {
            echo $equity_symbol . " ? " . ConfigConstants::NEW_LINE;
        }
    } else {
        if (EQ::isLocalServer()) {
            echo $equity_symbol . " symbol MD file is not writable in " . __METHOD__ . " ? Maybe, check permissions!" . ConfigConstants::NEW_LINE;
        }
    }
} else {
    $fh = fopen($new_filename, 'wb');
    fwrite($fh, $md_file_content);
    fclose($fh);
    if (EQ::isLocalServer()) {
        echo $equity_symbol . " front md file does not exit in " . __METHOD__ . " It's writing on the database now ?" . ConfigConstants::NEW_LINE;
    }
}

Ради СУХОГО принципа:

// It's smart to put the logging and similar tasks in a separate function, 
// after you end up writing the same thing over and over again.
public static function log($content)
{
    if (EQ::isLocalServer()) {
        echo $content;
    }
}

public static function writeFinalStringOnDatabase($equity_symbol, $md_file_content, $no_extension_filename)
{
    $md_file_content = $md_file_content . ConfigConstants::NEW_LINE . ConfigConstants::NEW_LINE;
    $dir = __DIR__ . ConfigConstants::DIR_FRONT_SYMBOLS_MD_FILES; // symbols front directory
    $new_filename = EQ::generateFileNameFromLeadingURL($no_extension_filename, $dir);
    $file_already_exists = file_exists($new_filename);

    if ($file_already_exists && !is_writable($new_filename)) {
        EQ::log($equity_symbol . " symbol MD file is not writable in " . __METHOD__ . " ? Maybe, check permissions!" . ConfigConstants::NEW_LINE);
    } else {
        $fh = fopen($new_filename,'wb'); // you should also check whether fopen succeeded
        fwrite($fh, $md_file_content); // you should also check whether fwrite succeeded

        if ($file_already_exists) {
            EQ::log($equity_symbol . " ? " . ConfigConstants::NEW_LINE);
        } else {
            EQ::log($equity_symbol . " front md file does not exit in " . __METHOD__ . " It's writing on the database now ?" . ConfigConstants::NEW_LINE);
        }

        fclose($fh);
    }
}

Возможная причина

tl; dr Слишком много накладных расходов из-за используемого API строки Zend .

Официальное Руководство по PHP гласит:

file_put_contents() идентичен вызову fopen(), fwrite() и fclose() для последовательной записи данных в файл.

Однако, если вы посмотрите на исходный код PHP на GitHub , вы увидите, что часть "запись данных" немного отличается в file_put_contents() и fwrite().

  • В функции fwrite прямой доступ к необработанным входным данным (= $md_file_content) для записи данных буфера в контекст потока:

    Строка 1171 :

ret = php_stream_write(stream, input, num_bytes);
  • В функции file_put_contents, с другой стороны, используется Zend string API (чего я раньше никогда не слышал). Здесь входные данные и длина инкапсулированы по некоторым причинам.

    Линия 662

numbytes = php_stream_write(stream, Z_STRVAL_P(data), Z_STRLEN_P(data));

(макросы Z_STR.... определены здесь , если вам интересно).

Итак, я подозреваю, что, возможно, Zend string API вызывает издержки при использовании file_put_contents.


примечание

Сначала я подумал, что каждый вызов file_put_contents() создает новый контекст потока, так как строки, связанные с созданием контекста, также немного отличались:

PHP_NAMED_FUNCTION(php_if_fopen) ( Ссылка ):

context = php_stream_context_from_zval(zcontext, 0);

PHP_FUNCTION(file_put_contents) ( Ссылка ):

context = php_stream_context_from_zval(zcontext, flags & PHP_FILE_NO_DEFAULT_CONTEXT);

Однако, при ближайшем рассмотрении, php_stream_context_from_zval выполняется эффективно с теми же параметрами, то есть первый параметр zcontext равен null, и так как вы не передаете flags file_put_contents, flags & PHP_FILE_NO_DEFAULT_CONTEXT становится также 0 и передается как второй параметр.

Итак, я предполагаю, что контекст потока по умолчанию повторно используется здесь при каждом вызове. Поскольку это, по-видимому, поток типа persistent, он не удаляется после вызова php_stream_close () . Таким образом, Fazit , как говорят немцы, заключается в том, что в обоих случаях очевидно, что нет никаких дополнительных издержек или одинаково одинаковых накладных расходов на создание или повторное использование потока.

Спасибо за чтение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...