2020-09-08
问题集锦 2020
包含了 2020 年遇到的所有值得记录,但又可以几句话回答清楚的问题!! 2020 年持续更新..
1. MySQL 错误代码 “SQLSTATE[HY000]”
经常会遇到 MySQL
相关的错误代码,都有 HY000
的标记,那么
-
什么是
HY000
?MySQL
服务端返回的,表示最近执行的SQL
语句状态的一种 -
这些状态都有哪些?
SQLSTATE
有五个字符组成,其中 '00000' 表示没有错误,大部分都和错误 code 有映射关系,其中HY000
表示通用错误, 对应几百个具体的错误代码。
2. MySQL 错误代码 “SQLSTATE[HY000]: General error: 2006 MySQL server has gone away”
最近线上执行比较久的脚本,经常会遇到此类错误,那么
-
什么是 “MySQL server has gone away”
“MySQL server has gone away”
指的是执行SQL
时,与MySQL
服务端失去连接。一般是因为服务端超时并关闭与客户端的连接。MySQL
服务端通过变量wait_timeout
来控制空连接的最长等待时间,默认是8 小时
,目前服务器的设置为 1 个小时。由于脚本大量调用第三方接口,经常各种超时、失败重试,导致执行时间长达几个小时。 -
如何重现错误?
$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”
最近某个接口,线上经常报这个错误,那么
-
什么是 “NAN or INF”?
用 0 除 0 的结果是 NAN;非 0 除 0 的结果是 INF ,
json_encode
NAN or INF 就会报E_WARNING
的警告错误。
php -r 'echo 0/0;'
php -r 'echo 1/0;'
但是除 0 ,php 只会报 Warning: Division by zero
的警告错误,在线上并没有发现这个错误。
-
php 什么时候不报
E_WARNING
错误?服务器的配置是
$ php -i|grep error_rep // error_reporting => 22525 => 22525
参考之前的文章
php -r 'echo decbin(22525);'
结果是 101011111111101 发现第二位 E_WARNING 标志位是 0 , 所有 Warning 没有报出来// 错误标志位如下.. 000000000000001 -> E_ERROR 000000000000010 -> E_WARNING ..
-
json_encode 如何处理这类报错
json_encode(NAN)
在 php7.4 之前本身不会报 warn,需要如下处理php < 7.4
json_encode(NAN); if (json_last_error() !== JSON_ERROR_NONE) { var_dump(json_last_error_msg()); }
php >= 7.4
json_encode(NAN, JSON_THROW_ON_ERROR); // JSON_THROW_ON_ERROR 标记,会在此次抛出 JsonException 异常 // 异常信息和之前的 json_last_error_msg() 一致
-
如何避免?
不管何时,再需要除法的地方,都要判断被除数是否为 0
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`
它们的区别是:
- exec\shell_exec\system\passthru 四个函数的第一个参数是 shell 可以执行的命令,如:
exec('php /usr/local/test.php')
pcntl_exec
就不能这么用,它的第一个参数是可执行的路径,在这里只能是 PHP
, 对应的方法是
pcntl_exec('php', ['/usr/local/test.php'])
- 获取外部脚本输出内容的方式的区别:
passthru\system
都会直接把外部脚本的输出传送到输出缓冲区,cli 就是打印到屏幕,fpm 则会输出到浏览器,所以输出外部脚本的内容是实时的。
shell_exec
只有一个参数,外部脚本执行完,会返回外部脚本的所有输出内容,所以输出外部脚本的内容是一次性的,有延迟的
exec
只会返回外部脚本的最后一行输出,如果要获取全部内容,需要传入第二个参数,外部脚本输出的每一行内容,对应第二个参数的一个数组元素,输出外部脚本的内容也是一次性的,有延迟的
passthru
除了和 system
一样实时输出内容,还支持二进制格式
- 回到问题 “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 上的问题:
- 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.
- 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.
- 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. 如何在日志不断写入的情况下,切割日志文件
mv + create
方式,这种方式不会丢失日志文件,由于Linux
操作系统每个进程打开文件,都会分配一个新的文件描述符对应这个文件,文件描述符中有个文件表,表中包含文件的状态信息、文件物理位置、文件 inode 信息等;每个进程都是单独的文件表,不会共用。文件表只包含物理位置,不包含文件路径,所以 mv 移动文件只是修改文件路径,进程写的仍然 mv 后的文件,create 新文件后需要通知,或者应用程序支持文件变更后重新打开文件即可写入新文件中。MySQL
、Nginx
都支持重新打开文件。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 ""