PHP7.0+Laravel环境下SQLSTATE[HY000]未知主机名问题求助
问题背景
此前服务运行正常,但现在周期性出现SQLSTATE[HY000] Unknown host machine name (severity 2)错误,目前只能通过执行sudo service php7.0-fpm restart临时解决,但几天后问题会再次复发。
已排查过以下类似问题,但均不适用当前场景:
- MSSQL VIA FreeTDS, ODBC, and Cpanel Unknown host machine name (severity 2)
- php dblib, Error: SQLSTATE[HY000] Unknown host machine name (severity 2)
环境信息
- Laravel版本:5.3
- PHP版本:
PHP 7.0.28-0ubuntu0.16.04.1 (cli) ( NTS ) Copyright (c) 1997-2017 The PHP Group Zend Engine v3.0.0, Copyright (c) 1998-2017 Zend Technologies with Zend OPcache v7.0.28-0ubuntu0.16.04.1, Copyright (c) 1999-2017, by Zend Technologies
相关配置文件
/etc/freetds/freetds.conf内容
# $Id: freetds.conf,v 1.12 2007/12/25 06:02:36 jklowden Exp $ # # This file is installed by FreeTDS if no file by the same # name is found in the installation directory. # # For information about the layout of this file and its settings, # see the freetds.conf manpage "man freetds.conf". # Global settings are overridden by those in a database # server specific section [global] # TDS protocol version #; tds version = 4.2 tds version = 8.0 # Whether to write a TDSDUMP file for diagnostic purposes # (setting this to /tmp is insecure on a multi-user system) dump file = /var/log/freetds.log ; debug flags = 0xffff # Command and connection timeouts ; timeout = 10 ; connect timeout = 10 # If you get out-of-memory errors, it may mean that your client # is trying to allocate a huge buffer for a TEXT field. # Try setting 'text size' to a more reasonable limit text size = 5242880 # A typical Sybase server [egServer50] host = symachine.domain.com port = 5000 tds version = 5.0 # A typical Microsoft server [egServer70] host = ntmachine.domain.com port = 1433 tds version = 7.0
/var/log/freetds.log部分日志
net.c:1259:GNUTLS: level 5: REC[0xbd2738ac20]: Preparing Packet Application Data(23) with length: 338 and min pad: 0 net.c:1259:GNUTLS: level 9: ENC[0xbd2738ac20]: cipher: AES-256-CBC, MAC: SHA384, Epoch: 1 net.c:1259:GNUTLS: level 11: WRITE: enqueued 421 bytes for 0xbd27345690. Total 421 bytes. net.c:1259:GNUTLS: level 11: WRITE FLUSH: 421 bytes in buffer. net.c:1242:in tds_push_func net.c:1259:GNUTLS: level 11: WRITE: wrote 421 bytes, 0 bytes left. net.c:1259:GNUTLS: level 5: REC[0xbd2738ac20]: Sent Packet[649] Application Data(23) in epoch 1 and length: 421 dblib.c:4639:dbsqlok(0xbd271fa580) dblib.c:4669:dbsqlok() not done, calling tds_process_tokens() token.c:540:tds_process_tokens(0xbd27345690, 0x7ffd4b1c45a0, 0x7ffd4b1c45a4, 0x6914) util.c:156:Changed query state from PENDING to READING net.c:1201:in tds_pull_func net.c:1259:GNUTLS: level 10: READ: Got 5 bytes from 0xbd27345690 net.c:1259:GNUTLS: level 10: READ: read 5 bytes from 0xbd27345690 net.c:1259:GNUTLS: level 10: RB: Have 0 bytes into buffer. Adding 5 bytes. net.c:1259:GNUTLS: level 10: RB: Requested 5 bytes net.c:1259:GNUTLS: level 5: REC[0xbd2738ac20]: SSL 3.3 Application Data packet received. Epoch 0, length: 96 net.c:1259:GNUTLS: level 5: REC[0xbd2738ac20]: Expected Packet Application Data(23) net.c:1259:GNUTLS: level 5: REC[0xbd2738ac20]: Received Packet Application Data(23) with length: 96 net.c:1201:in tds_pull_func net.c:1259:GNUTLS: level 10: READ: Got 96 bytes from 0xbd27345690 net.c:1259:GNUTLS: level 10: READ: read 96 bytes from 0xbd27345690 net.c:1259:GNUTLS: level 10: RB: Have 5 bytes into buffer. Adding 96 bytes. net.c:1259:GNUTLS: level 10: RB: Requested 101 bytes net.c:1259:GNUTLS: level 5: REC[0xbd2738ac20]: Decrypted Packet[3886] Application Data(23) with length: 17 net.c:557:Received header 0000 04 01 00 11 00 73 01 00- |.....s..| net.c:611:Received packet 0000 04 01 00 11 00 73 01 00-fd 10 00 c5 00 01 00 00 |.....s.. ........| 0010 00 - |.| token.c:555:processing result tokens. marker is fd(DONE) token.c:2339:tds_process_end: more_results = 0 was_cancelled = 0 error = 0 done_count_valid = 1 token.c:2355:tds_process_end() state set to TDS_IDLE util.c:156:Changed query state from READING to IDLE token.c:2370: rows_affected = 1 util.c:104:logic error: cannot change query state from IDLE to PENDING dblib.c:4707:dbsqlok() end status is SUCCEED dblib.c:4718:dbsqlok() end status was success dblib.c:1668:dbresults(0xbd271fa580) dblib.c:1674:dbresults: dbresults_state is 5 (_DB_RES_SUCCEED) dblib.c:1657:dbresults returning 1 (SUCCEED) dblib.c:2761:dbcount(0xbd271fa580) dblib.c:1813:dbnumcols(
根治方案
从你的情况来看,周期性出现问题且重启PHP-FPM能解决,大概率是连接池泄漏或者FreeTDS的DNS缓存/连接管理bug导致的,试试下面这几个方法:
1. 替换数据库主机名为IP地址
周期性的"Unknown host"很可能是DNS解析缓存失效,FreeTDS在初始化连接时缓存了DNS记录,后续DNS更新后没有重新解析。直接在Laravel数据库配置(config/database.php)里把数据库主机名换成IP地址,绕开DNS解析问题。
2. 调整FreeTDS的连接超时与保持设置
修改/etc/freetds/freetds.conf的[global]部分,添加或调整以下参数:
connect timeout = 15 timeout = 30 keepalive = 1 tds version = 7.4 # 根据你的SQL Server版本调整:SQL Server 2016+用7.4,2012用7.2,2008用7.1
connect timeout:限制连接建立的超时时间,避免无效连接挂起keepalive:启用TCP keepalive,检测失效的连接并自动关闭- 建议指定具体的TDS版本,而非兼容模式的
8.0,匹配SQL Server版本能提升连接稳定性
3. 优化PHP-FPM的进程池配置
PHP-FPM进程长期运行可能积累失效的数据库连接,修改/etc/php/7.0/fpm/pool.d/www.conf(根据你的pool名称调整):
pm = dynamic pm.max_children = 50 pm.start_servers = 10 pm.min_spare_servers = 5 pm.max_spare_servers = 20 pm.max_requests = 500 # 关键:每个进程处理500个请求后自动重启,清除无效连接
设置pm.max_requests让PHP-FPM进程定期重启,能有效缓解周期性的连接泄漏问题。
4. 升级FreeTDS版本
你当前使用的FreeTDS配置文件是2007年的旧版本,在连接管理、DNS解析上可能存在已知bug。升级到最新稳定版:
sudo apt-get update sudo apt-get install --only-upgrade freetds-bin freetds-dev
升级后重启PHP-FPM和Web服务器,观察问题是否复发。
5. 检查Laravel的数据库连接配置
如果启用了persistent持久连接,可能会导致连接长期占用而失效。暂时在config/database.php里设置'persistent' => false,关闭持久连接后观察问题是否改善。
验证方法
修改配置后不要立即重启,观察3-7天看问题是否还会出现。如果仍有问题,可以开启FreeTDS的详细日志(取消debug flags = 0xffff的注释),捕捉错误发生时的完整日志,进一步定位根因。
内容的提问来源于stack exchange,提问作者sulaiman sudirman

