Varobj

2020-09-08

问题集锦 2020



包含了 2020 年遇到的所有值得记录,但又可以几句话回答清楚的问题!! 2020 年持续更新..

1. MySQL 错误代码 “SQLSTATE[HY000]”

经常会遇到 MySQL 相关的错误代码,都有 HY000 的标记,那么


2. MySQL 错误代码 “SQLSTATE[HY000]: General error: 2006 MySQL server has gone away”

最近线上执行比较久的脚本,经常会遇到此类错误,那么

$res = $model->getReadConnection()->execute('select @@wait_timeout');
echo $res['@@wait_timeout'];
// output: 3600
$connection = $model->getReadConnection();
$connection->execute('set @@wait_timeout=60');

sleep(65);
$connection->execute('select 1');
// SQLSTATE[HY000]: General error: 2006 MySQL server has gone away

3. PHP 错误代码 “One or more NAN or INF values in the value to be encoded”

最近某个接口,线上经常报这个错误,那么

php -r 'echo 0/0;'
php -r 'echo 1/0;'

但是除 0 ,php 只会报 Warning: Division by zero 的警告错误,在线上并没有发现这个错误。


4. PHP array_intersect($a, $b, )会报错吗?

由于 PhpStorm 集成的 PHP 版本是 7.4 ,此种写法没有提示错误,提交到线上(PHP7.2)就报了语法错误,经验证 7.4 确实不会报错,而且可以正常运行,因吹斯汀! 而且 array_merge 等其他函数也是一样


5. str_replace(["\r", "\n", "\r\n"], [""], $str) 是否生效

Yes

6. PHP 脚本能否针对 ctrl + c 的中断信号做特殊处理,如删除 redis 锁等操作?如何处理?

可以,通过 pcntl 相关扩展。linux 操作系统通过 kill 发送中断信息给程序,默认是 SIGTERM(15)

# kill -L
1) SIGHUP     2) SIGINT     3) SIGQUIT     4) SIGILL     5) SIGTRAP
6) SIGABRT     7) SIGBUS     8) SIGFPE     9) SIGKILL    10) SIGUSR1
11) SIGSEGV    12) SIGUSR2    13) SIGPIPE    14) SIGALRM    15) SIGTERM
16) SIGSTKFLT    17) SIGCHLD    18) SIGCONT    19) SIGSTOP    20) SIGTSTP
21) SIGTTIN    22) SIGTTOU    23) SIGURG    24) SIGXCPU    25) SIGXFSZ
26) SIGVTALRM    27) SIGPROF    28) SIGWINCH    29) SIGIO    30) SIGPWR
31) SIGSYS    34) SIGRTMIN    35) SIGRTMIN+1    36) SIGRTMIN+2    37) SIGRTMIN+3
38) SIGRTMIN+4    39) SIGRTMIN+5    40) SIGRTMIN+6    41) SIGRTMIN+7    42) SIGRTMIN+8
43) SIGRTMIN+9    44) SIGRTMIN+10    45) SIGRTMIN+11    46) SIGRTMIN+12    47) SIGRTMIN+13
48) SIGRTMIN+14    49) SIGRTMIN+15    50) SIGRTMAX-14    51) SIGRTMAX-13    52) SIGRTMAX-12
53) SIGRTMAX-11    54) SIGRTMAX-10    55) SIGRTMAX-9    56) SIGRTMAX-8    57) SIGRTMAX-7
58) SIGRTMAX-6    59) SIGRTMAX-5    60) SIGRTMAX-4    61) SIGRTMAX-3    62) SIGRTMAX-2
63) SIGRTMAX-1    64) SIGRTMAX

php 通过 pcntl_signal 注册不同的信号处理器,用来处理接收到不同信号的操作。

declare(ticks=1);

pcntl_signal(SIGTERM, [$this, 'signal_quit_handler']); // kill

pcntl_signal(SIGINT, [$this, 'signal_quit_handler']); // Ctrl-C, kill -2

在这里必须首选声明 ticks=1,表示每执行一个基础指令就会执行 register_tick_function() 注册的函数,或者执行 pcntl_signal 的信号处理器函数。

特别说明:假如注册了 SIGINT 的相关信号处理器,但没有声明 ticks=1, 程序运行时 ctrl+c 并不会执行 signal_quit_handler,同时也不会退出(默认接收到 kill 信号会退出程序),ticks=N 的原理是 php 底层每执行 N 条指令,就会调用注册的相关函数,如果没有声明 ticks ,则没法触发指定函数。但这种方式效率太低, PHP 新版本可以通过手动调用 pcntl_signal_dispatch() 函数的方式手动触发。例如业务代码包含一个循环,每次循环开始,手动调用 pcntl_signal_dispatch

while(true) {
  pcntl_signal_dispatch();
  // biz code
}

示例1:

function test() {
    echo 'this is test ' . PHP_EOL;
}

pcntl_signal(SIGINT, 'test');

$i = 0;
while ($i++ < 3) {
    echo $i . PHP_EOL;
    sleep(30)
}

执行之后,ctrl + c

// output:
1
^C2
3

这种情况即没有声明 ticks , 也没有手动 dispatch 所以不会执行 test 函数中的内容。

示例2:

function test()
{
    echo 'this is test ' . PHP_EOL;
    exit;
}

pcntl_signal(SIGINT, 'test');
//declare(ticks=1);

$i = 0;
while ($i++ < 3) {
    pcntl_signal_dispatch();
    echo $i . PHP_EOL;
    sleep(30);
}

执行之后,ctrl + c

// output:
1
^Cthis is test

7. PHP 如何异步启动脚本,非子进程模式

php 有多种方式调用外部脚本,如:

exec($command, array &$output = null, &$return_var = null)

shell_exec($command)

system($command, &$return_var = null)

passthru($command, &$return_var = null)

pcntl_exec($path, array $args = null, array $envs = null)

echo `ls /usr/local/bin`

它们的区别是:

  1. exec\shell_exec\system\passthru 四个函数的第一个参数是 shell 可以执行的命令,如:
exec('php /usr/local/test.php')

pcntl_exec 就不能这么用,它的第一个参数是可执行的路径,在这里只能是 PHP , 对应的方法是

pcntl_exec('php', ['/usr/local/test.php'])
  1. 获取外部脚本输出内容的方式的区别:

passthru\system 都会直接把外部脚本的输出传送到输出缓冲区,cli 就是打印到屏幕,fpm 则会输出到浏览器,所以输出外部脚本的内容是实时的。

shell_exec 只有一个参数,外部脚本执行完,会返回外部脚本的所有输出内容,所以输出外部脚本的内容是一次性的,有延迟的

exec 只会返回外部脚本的最后一行输出,如果要获取全部内容,需要传入第二个参数,外部脚本输出的每一行内容,对应第二个参数的一个数组元素,输出外部脚本的内容也是一次性的,有延迟的

passthru 除了和 system 一样实时输出内容,还支持二进制格式

  1. 回到问题 “PHP 如何异步启动脚本?”

既然是异步,就不能等待输出结果执行完,就需要返回,可以通过下面的方法

exec('php /usr/local/test.php > /dev/null 2>&1 &');

如果想获取到异步启动的脚本的 pid ,怎么办?

exec('php /usr/local/test.php > /dev/null 2>&1 & echo $!', $output);

echo $output[0];

8. free -h 中 Cached memory 和 Used memory 有什么区别?

cached 是系统为了缓存磁盘缓存的,不是应用程序使用的,used 是应用程序使用的,当应用程序需要更多内存时,系统会从 cached 中借所需大小,所以无需担心 cached 过大。

参考地址


9. Why cached memory 这么大?

真不知道

10. cached 太大有什么问题吗?

摘自 quora 上的问题:

  1. Large caches means that you need more hardware to implement and also bigger control blocks. If you know the cell structure of SRAM (cache) cell then it needs 6 MOS to store and access 1 bit, whereas DRAM (main memory) cell needs 1 capacitor & MOS to store and access 1 bit. So if you need bigger caches then you are increasing the area and cost.
  2. Bigger caches means that large control blocks which means that you need more time to search the desired cache lines. This means more clock cycles to fetch cache line, which defeats the purpose of caches.
  3. Biggest question, why do we need bigger caches. Companies perform extensive benchmarking to know the sweet spot for cache size and associativity of cache for different market (embedded, general purpose, server etc on basis of their workloads and requirements). So anything bigger then that is overkill.

quora 上相关问题 Why-is-having-too-much-cache-memory-not-good


11. 如何清除 cache , buffers

sync; echo 1 > /proc/sys/vm/drop_caches


12. Redis 等连接带持久化参数等有什么作用?

在 php-fpm 模式下,开启持久化连接,在每次请求生命周期结束之后,fpm 进程中保留当前连接,下一次请求进来不用调用 connection 连接 Redis 服务端,可以直接复用上次的连接。

所以: pconnect() 函数会在 fpm 进程已经建立 Redis 连接的情况下,复用连接;而 connect() 函数不管 fpm 进程是否已经建立 Redis 连接,都会重新发起连接。


13. MySQL 的 straight_join 作用

介绍文章


14. 如何在日志不断写入的情况下,切割日志文件

  1. mv + create 方式,这种方式不会丢失日志文件,由于 Linux 操作系统每个进程打开文件,都会分配一个新的文件描述符对应这个文件,文件描述符中有个文件表,表中包含文件的状态信息、文件物理位置、文件 inode 信息等;每个进程都是单独的文件表,不会共用。文件表只包含物理位置,不包含文件路径,所以 mv 移动文件只是修改文件路径,进程写的仍然 mv 后的文件,create 新文件后需要通知,或者应用程序支持文件变更后重新打开文件即可写入新文件中。 MySQLNginx 都支持重新打开文件。
  2. copy + truncate 方式, PHP 的 fopen 、fwrite 并不会重新打开文件,所以可以通过 copy 文件到新文件,然后当前日志清空,copy 的时间可能很慢,清空很快,所以中间会有一段时间的日志会丢失。另外如果 PHP 使用的是 file_put_content 函数写入日志,mv + create 方式也可以保证日志不丢。

15. redis 如何禁用危险的命令,如 flushdb 等

6.0 版本才开始支持 Acl 权限控制,之前只能通过重命名的方式,覆盖危险命令

使用 rename-command 命令

// vim /etc/redis.conf

rename-command flushdb ""
rename-command flushall ""
rename-command keys ""