我们有一台旧服务器,直到昨天还在运行 2016 版本的 Ubuntu。服务器速度超级快(不到 1 秒就提供了一个复杂的页面)。由于我们需要 PHP8.2,我们决定升级到 Ubuntu22。
升级后系统很慢,同样的页面需要30秒左右才能生成!!!!
经过调查,我认为问题是由于MySQL造成的。升级之前我们的数据库如下:
-- MySQL dump 10.13 Distrib 5.7.33, for Linux (x86_64)
--
-- Host: localhost Database: website
-- ------------------------------------------------------
-- Server version 5.7.33-0ubuntu0.16.04.1
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
--
-- Table structure for table `articleBookmarks`
--
DROP TABLE IF EXISTS `articleBookmarks`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `articleBookmarks` (
`bookmarkId` int(11) NOT NULL AUTO_INCREMENT,
`userId` int(11) DEFAULT NULL,
`articleId` char(16) DEFAULT NULL,
`category` int(11) DEFAULT NULL,
`addDate` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`bookmarkId`)
) ENGINE=InnoDB AUTO_INCREMENT=99 DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;
升级后我有
-- MySQL dump 10.13 Distrib 8.0.36, for Linux (x86_64)
--
-- Host: localhost Database: website
-- ------------------------------------------------------
-- Server version 8.0.36-0ubuntu0.22.04.1
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!50503 SET NAMES utf8 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
--
-- Table structure for table `articleBookmarks`
--
DROP TABLE IF EXISTS `articleBookmarks`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `articleBookmarks` (
`bookmarkId` int NOT NULL AUTO_INCREMENT,
`userId` int DEFAULT NULL,
`articleId` char(16) DEFAULT NULL,
`category` int DEFAULT NULL,
`addDate` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`bookmarkId`)
) ENGINE=InnoDB AUTO_INCREMENT=135 DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;
看来ubuntu决定在不询问的情况下将字符集从utf8更改/转换为utf8mb4!
因此,数据库错误地显示了所有表情符号和空间符号。例如,微笑表情符号现在显示为“ðŸ~‚”。
此外,我无法在数据库中插入更多记录。我收到以下错误:
[2024 年 3 月 26 日星期二 15:27:25.623713 2024] [php:error] [pid 4341] [client 40.xx.xxx.xxx:52355] PHP 致命错误:未捕获 mysqli_sql_exception:参数中不可能从排序规则 utf8mb4_0900_ai_ci 转换为 latin1_swedish_ci /var/www/html/MyPHPfunction.php:8630 堆栈跟踪: #0 /var/www/html/MyPHPfunction.php(8630): mysqli_stmt->execute() #1 /var/www/html/funct2.php(398): show_search_results() #2 {主要} 扔进 /var/www/html/funct2.php 第 8630 行
我找不到任何有 latin1_swedish_ci 的桌子
有人知道如何解决这个问题吗?整个服务器目前在某种程度上毫无用处:(
在进行此类大规模更改之前确认您拥有可用的备份,并在针对任何生产数据运行之前在副本上测试更改,这一点非常重要。
您很可能最终在 latin1 列中存储了 UTF-8 字符。在数据库服务器升级之前,连接字符集和列字符集都是 latin1,因此在读取和/或写入数据库时不会发生隐式字符转换。现在默认字符集是utf8mb4,连接是utf8mb4,但列是latin1。这会导致隐式字符转换,从而破坏您的数据。
要确认上述假设,请针对您知道包含显示不正确的数据的表/列运行以下 SELECT。通过 utf8mb4 连接执行此操作非常重要。
SELECT `name`, CONVERT(CONVERT(`name` USING binary) USING utf8mb4)
FROM `your_db_name`.`test1`;
我希望第一列包含损坏的版本,第二列是正确的。
如果上述情况证实了怀疑的情况,则可以使用 information_schema 来编写所有 ALTER TABLE 语句。像这样的东西:
SELECT CONCAT(
'ALTER TABLE `', TABLE_SCHEMA, '`.`', TABLE_NAME, '`\n',
GROUP_CONCAT(
'\tMODIFY COLUMN `', COLUMN_NAME, '` ',
IF(DATA_TYPE IN ('varchar', 'char'), CONCAT('varbinary(', CHARACTER_MAXIMUM_LENGTH, ')'), 'blob'),
IF(IS_NULLABLE = 'NO', ' NOT NULL', ''),
IF(COLUMN_DEFAULT IS NOT NULL, CONCAT('DEFAULT \'', COLUMN_DEFAULT, '\''), '')
SEPARATOR ',\n'
),
';\n'
'ALTER TABLE `', TABLE_SCHEMA, '`.`', TABLE_NAME, '`\n',
GROUP_CONCAT(
'\tMODIFY COLUMN `', COLUMN_NAME, '` ',
COLUMN_TYPE, ' CHARACTER SET utf8mb4',
IF(IS_NULLABLE = 'NO', ' NOT NULL', ''),
IF(COLUMN_DEFAULT IS NOT NULL, CONCAT('DEFAULT \'', COLUMN_DEFAULT, '\''), '')
SEPARATOR ',\n'
),
',\n\tDEFAULT CHARACTER SET utf8mb4;\n'
) AS `alter_stmt`
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'your_db_name'
AND DATA_TYPE IN ('varchar', 'char', 'text')
AND CHARACTER_SET_NAME = 'latin1'
GROUP BY TABLE_NAME;
给出一个像这样的简单表格:
CREATE TABLE `your_db_name`.`test1` (
`id` int NOT NULL,
`name` varchar(255) NOT NULL,
`description` text NOT NULL,
`col1` char(2) DEFAULT NULL,
`status` tinyint NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
上面的查询将输出类似:
ALTER TABLE `your_db_name`.`test1`
MODIFY COLUMN `name` varbinary(255) NOT NULL,
MODIFY COLUMN `description` blob NOT NULL,
MODIFY COLUMN `col1` varbinary(2);
ALTER TABLE `your_db_name`.`test1`
MODIFY COLUMN `name` varchar(255) CHARACTER SET utf8mb4 NOT NULL,
MODIFY COLUMN `description` text CHARACTER SET utf8mb4 NOT NULL,
MODIFY COLUMN `col1` char(2) CHARACTER SET utf8mb4,
DEFAULT CHARACTER SET utf8mb4;
如果需要,您可以修改信息架构查询以添加适当的排序规则和列注释。