MySQL导入全球旅游城市数据包:从环境检查到查询优化全攻略
简介这份资源是一套面向餐饮旅游行业的数据分析基础包将全球旅游城市核心信息整理成一份可直接使用的MySQL数据库文件。压缩包内共1个SQL文件整包大小仅133KB却包含全球6000余条旅游城市数据涵盖城市名称、地理位置、景点以及餐饮业相关字段非常适合需要快速获取初始数据集进行旅游趋势分析、餐饮分布研究或课程实训的开发人员与学习者。SQL文件采用建表与插入语句的标准写法导入MySQL后即可直接运行免去了逐条录入与格式转换的麻烦用户可在此基础上练习单表查询、聚合统计、多表关联等常用操作也能将其接入Web应用或可视化工具进一步完成业务展示与决策分析。目前已有317人学习下载对于正在接触MySQL、希望积累真实数据处理经验的人群是一份轻量且实用的入门资料。1. 全球旅游城市数据6000条可直接导入的MySQL数据包能做什么想做旅游推荐系统或者城市数据看板最耽误进度的往往不是算法调参而是手上没有一份干净的经纬度、景点和消费数据。这个数据包把全球 6000 条旅游城市数据整理成 MySQL 可导入的 SQL 语句解压、建库、导入三步就能跑起来不用写爬虫也不用逐条清洗。对做原型验证、课程设计的人来说省下的是实实在在的周末时间。数据包含城市名称、所属大洲/国家、经纬度、典型景点、时区、平均消费水平等字段SQL 语句已按可执行标准写好导入后直接跑 SELECT。从零建这种规模的城市维表少说要写几百行 INSERT手工整理一定会出错。尤其是跨语言的字段比如中文城市名和英文名对照靠人工逐条录不现实。适合三类人准备做旅游项目但缺基础数据的开发者需要全球城市样本做统计分析的人想用真实数据练手 MySQL 导入和查询优化的新人。后面操作按 Windows 加 MySQL 8.0 讲解Linux 命令差异会在对应位置说明。2. 导入前准备MySQL环境检查与建库选型导入 SQL 是最容易低估环境因素的操作。同一份 SQL 文件在 MySQL 5.7 跑得好好的换到 8.0 可能因为默认字符集变了报错客户端工具编码不对导入后中文全变成问号。所以在解压数据包之前先把环境摸清楚这一步能避免后面九成的问题。2.1 环境要求与版本兼容性确认这份数据包的核心内容是 INSERT 和 CREATE TABLE 语句不含存储过程、触发器这类高级对象所以版本兼容性主要看三点数据库字符集、sql_mode 严格模式、max_allowed_packet 上限。我用过的数据包里出现过一张表用了中文表名在 MySQL 5.7 下建表成功换到 8.0 会因为表名编码校验失败。所以强烈建议先确认目标实例的版本。MySQL 8.0 的默认字符集是 utf8mb45.7 默认是 latin1。数据包里的中文注释和中文城市名在 utf8mb4 下才能完整保存。我一般会在导入前把服务器、数据库、客户端三级环境统一成 utf8mb4避免字符串截断报错。注意这里说的三级环境不是同一个概念服务器变量 character_set_server 决定默认建库字符集客户端的 character_set_client 决定你发送的字节怎么解释两者不一致就会出现“库里存的是对的看起来是乱的”这种怪现象。sql_mode 也需要留意。5.7 默认的 sql_mode 里没有严格模式8.0 默认是严格模式。如果 SQL 文件里的 INSERT 语句有个别字段少值严格模式下直接报错中断非严格模式会补默认值继续跑。我的建议是维持严格模式遇到报错用第 5 章的排查方法处理不要一上来就关掉严格模式。关闭严格模式能绕过这一次报错但会把数据质量问题掩盖掉后面查起来更痛苦。max_allowed_packet 控制单次发送给服务器的数据包上限。数据包里 6000 条数据如果合并成一条大 INSERT 执行包大小可能超过默认的 64MB导致报错。可以在 MySQL 配置文件中把该值调到 128M。在 MySQL 8.0 中该参数默认值是 64MB但对大字段多的表依然可能不够。先登录 MySQL 检查这几个关键变量SHOW VARIABLES LIKE character_set_server; SHOW VARIABLES LIKE sql_mode; SHOW VARIABLES LIKE max_allowed_packet;第一行看字符集是否为 utf8mb4第二行看是否启用了严格模式sql_mode 是否包含 STRICT_TRANS_TABLES第三行看单次最大包大小。这三个变量决定了后续导入会不会在中途翻车。如果 character_set_server 不是 utf8mb4可以在 my.cnf 或 my.ini 的 [mysqld] 段加上 character-set-serverutf8mb4然后重启服务。Linux 下配置文件位置一般在 /etc/mysql/my.cnfWindows 下一般在 MySQL 安装目录的 my.ini。注意这里改的是服务器端配置客户端工具自己的字符集还要单独设置。命令行客户端可以在连接参数里指定图形化工具在连接属性里指定后面会分别说明。2.2 建立目标数据库并设置字符集建议单独建一个数据库存放这套数据不要混进业务库。原因很简单这种整理好的静态数据包导入后一般不会频繁更新单独建库方便整体备份、导出和清理。万一哪天要把旧数据清掉重导直接 DROP DATABASE 再重建不影响业务表。数据库名用英文小写。Windows 下 MySQL 对库名大小写不敏感但 Linux 下敏感统一小写可以少踩坑。建库语句如下CREATE DATABASE IF NOT EXISTS travel_cities DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;CREATE DATABASE 语句里同时指定 CHARACTER SET 和 COLLATE 两个参数。utf8mb4 是字符集能存下中文、日文、韩文以及 emojiutf8mb4_unicode_ci 是排序规则基于 Unicode 编码做比较排序结果更符合语言习惯。相比 utf8mb4_general_ciunicode_ci 在处理多语言混合数据时更准确代价是排序略慢对 6000 条的数据量来说完全无感。建库之后可以在当前会话里确认默认字符集已经生效USE travel_cities; SELECT character_set_database, collation_database;这两行返回的值应该分别是 utf8mb4 和 utf8mb4_unicode_ci。如果返回的是 latin1 之类的旧值检查一下是不是建库语句里漏了字符集参数。很多时候建库语句是从旧文档里复制过来的字符集参数还是老的 latin1导致后面导中文就出问题。2.3 解压数据包并确认文件结构拿到 .rar 文件后先解压而不是直接改扩展名。WinRAR、7-Zip 都能解压命令行爱好者也可以用 unrar 或 7z 命令。解压后通常得到一个 .sql 文件也可能附带一个数据字典说明文件比如字段注释的 PDF 或 Excel。数据字典建议花十分钟读一遍里面有字段类型和含义比对着代码猜字段靠谱得多。文件大小是第一个判断信号。6000 条纯 INSERT 的 SQL文件大小通常在 2MB 到 10MB 之间。如果只有几十 KB那大概率是压缩率特别高或者数据本身不完整需要警惕。在命令行里用以下方式查看文件头和文件编码Linux 或 macOS 下用 file 命令# 检查 SQL 文件的编码类型 file tourism_cities.sqlWindows 下用 PowerShell# 读取 SQL 文件前 20 行确认文件头和编码 Get-Content tourism_cities.sql -TotalCount 20 -Encoding UTF8file 命令能同时识别文件编码和换行符格式比如输出 UTF-8 Unicode text 说明是正常的 UTF-8 文件如果显示 ISO-8859 或 ASCII说明文件里的中文字符可能不是 UTF-8 编码导入后大概率乱码。PowerShell 的 Get-Content 加 -TotalCount 20 只看前 20 行能快速确认文件头和关键注释。查看前 20 行是为了确认 SQL 文件是否包含 CREATE DATABASE 或 USE 语句。有的数据包会自带 USE travel_cities;那就不需要手动切库有的只有 CREATE TABLE 和 INSERT导入时必须提前建好库并指定目标库。我习惯先看文件头再动手具体操作是打开文本编辑器查看前 30 行确认三件事建表语句是否完整、表名是什么、INSERT 的字段顺序是否和表结构一致。确认完心里就有底了后面导入就是一条命令的事。如果解压后是多个 .sql 文件不用慌这通常是按数据域拆分的比如城市基础表和景点表分开存放。按依赖顺序依次导入即可先导主表再导子表具体顺序会在第 5 章外键问题里详细讲。3. 导入执行从 SQL 文件到可查询的数据表导入阶段的核心目标是让数据包里的 SQL 文件变成数据库里真实存在的数据表并且行数与标注一致。这里提供三种方式命令行、图形化工具、以及受限环境下的分批导入。三种方式不是互斥的很多时候先用命令行跑一遍失败后再换可视化工具定位问题反而效率更高。3.1 命令行导入最稳妥的 MySQL 导入方式命令行导入是最直接的方式不依赖图形界面出错信息也最完整。在 Windows 的命令提示符CMD里执行下面命令PowerShell 7 也支持同样的语法# 通过命令行工具导入 SQL 文件--default-character-set 防止中文乱码 mysql -uroot -p travel_cities --default-character-setutf8mb4 tourism_cities.sql把 tourism_cities.sql 换成实际文件名。这里有几个参数值得说明-uroot 指定用户名-p 表示交互式输入密码travel_cities 是目标数据库名--default-character-setutf8mb4 强制客户端以 utf8mb4 编码读取 SQL 文件并发送给服务端这是避免中文乱码的关键参数。建议在本地测试环境先用一个小样本文件验证参数没问题再导入全量数据。如果文件路径含空格用双引号包裹整个路径mysql -uroot -p travel_cities --default-character-setutf8mb4 D:\data pack\tourism_cities.sqlWindows 下路径有空格时尖括号后的文件路径必须加引号否则命令行会按空格拆分成多个参数导致文件找不到。Linux 下路径分隔符是斜杠逻辑相同。导入过程中如果报错信息太多屏幕上滚一下就过去了这时可以把输出同时写到日志文件里。Linux 下用 tee 命令# 把导入过程的输出同时写入 import.log方便事后排查 mysql -uroot -p travel_cities --default-character-setutf8mb4 tourism_cities.sql 21 | tee import.log21 把标准错误合并到标准输出tee 同时输出到屏幕和 import.log。这样即使出错也能事后慢慢翻日志。Windows 下可以用 PowerShell 的 Tee-Object 或者直接重定向到文件。导入完成后没有任何输出就是成功。如果看到 ERROR 开头的信息说明执行中断需要根据错误码定位问题具体见第 5 章。还有一个常见情况是命令执行后没有任何提示但表里只有部分数据这种情况通常是因为 SQL 文件里带了 ON DUPLICATE KEY UPDATE 或者 REPLACE 语句导致重复行被吞掉需要查表的实际行数来判断。Linux 服务器上的命令几乎一致。导入前用 mysql -uroot -p -e USE travel_cities; SELECT 1; 测试一下连接能返回 1 说明连接正常。线上环境建议加上 --single-transaction 参数保证导入期间的一致性虽然对 6000 条数据影响不大但是个好习惯mysql -uroot -p travel_cities --default-character-setutf8mb4 --single-transaction tourism_cities.sql3.2 图形化工具导入Navicat 与 Workbench 的操作差异如果不想记命令行用 Navicat 或 MySQL Workbench 也能完成导入。两者的操作路径不同踩坑点也不一样。Navicat 的操作是连接数据库后双击打开 travel_cities 库右键选择“运行 SQL 文件”选中解压后的 .sql 文件点击开始。运行前有一个选项需要注意是否勾选“遇到错误时继续”。我的习惯是不勾选让它在第一条错误处停下方便定位问题。如果勾选了错误会累积到最后一次性显示排查起来非常被动。MySQL Workbench 的导入方式不同它没有直接的“运行 SQL 文件”菜单按钮常见做法是通过 Server 菜单下的 Data Import或者直接打开 SQL 文件后执行全部语句。直接打开文件执行对 10MB 内的 SQL 文件没问题但要注意 Workbench 默认会限制单次查询的最大执行时间遇到大事务可能中途停止。如果停止需要到 Edit 菜单下的 Preferences 里调大 DBMS 连接超时时间。另一个差别是编码处理。Navicat 在运行 SQL 文件时默认使用连接的字符集如果连接字符集是 utf8mb4 就没问题。Workbench 打开 SQL 文件时会读取文件编码如果文件是 UTF-8 无 BOM 且系统区域设置不是中文环境可能识别成 latin1导入后中文乱码。遇到这种情况可以在打开文件时手动指定编码或者用 SET NAMES utf8mb4 强制当前会话编码。图形化工具的另一个坑是事务处理Navicat 运行 SQL 文件时默认自动提交数据量大时每几秒提交一次如果中途手动停止已提交的部分会保留未提交的部分回滚。而 Workbench 执行整个文件时通常是一个隐式事务中途失败可能全部回滚。理解这个差异能帮你判断导入失败后哪些数据还在。我的选择建议是数据量在几万条以内的场合Navicat 交互速度更快需要精确控制执行过程时用命令行Workbench 适合已经用它做日常开发的人不必为了导入单独装新工具。3.3 导入校验行数、表结构与索引检查无论用哪种方式导入完成后都必须做三层校验否则无法确认数据是否完整。第一步查表确认数据库里出现了几张表表名是否和预期一致USE travel_cities; SHOW TABLES;第二步查行数分别统计每张表的记录数和标题标注的 6000 对照SELECT COUNT(*) FROM city_info;city_info 是表名示例实际操作时以 SQL 文件里的 CREATE TABLE 语句为准。如果导入后只有几十行那肯定有问题。行数对不上时用 SELECT COUNT(*) 和 SELECT COUNT(DISTINCT 主键列) 对照能判断是重复数据还是漏插。如果有多张表逐张 COUNT 比较慢可以借助 information_schema 批量查看行数-- table_rows 是估算值适合快速发现数量级明显的错误 SELECT table_name, table_rows FROM information_schema.tables WHERE table_schema travel_cities;注意 information_schema 的 table_rows 在 InnoDB 引擎下是估算值不一定精确但能快速发现数量级明显的问题。精确值还是得用 COUNT(*)。第三步查索引和抽样数据SHOW INDEX FROM city_info; SELECT * FROM city_info LIMIT 10;SHOW INDEX 的输出里能看到主键、唯一键和普通索引。如果表名是 city_info、字段里有 city_id一般会带 PRIMARY KEY。LIMIT 10 抽查前 10 行的中文显示是否正常经纬度是否在合理范围国家字段是否为空。这一步是花钱最少、收益最高的验收动作不要跳过。4. 数据结构拆解表设计、字段含义与数据质量很多开发者拿到 SQL 文件直接导入SELECT * 能出结果就开始联调等真正要用某个字段时才发现经纬度格式不对、城市名重复、少数行关键字段为空。前期花半小时读一下表结构后期能省下大量排查时间。4.1 城市主表设计与字段类型分析这套数据包的核心表是城市基础信息表典型字段设计如下字段名类型允许为空典型内容city_idINT UNSIGNED否自增主键city_name_cnVARCHAR(100)否中文城市名city_name_enVARCHAR(100)是英文城市名continentVARCHAR(30)否所属大洲countryVARCHAR(50)否所属国家或地区longitudeDECIMAL(9,6)是经度latitudeDECIMAL(9,6)是纬度timezoneVARCHAR(50)是时区标识budget_levelTINYINT是1-5 消费等级DECIMAL(9,6) 是经纬度字段的合理选择。精度到小数点后 6 位对应大约 0.1 米的定位误差对旅游场景绰绰有余。如果用 FLOAT经纬度在比较和聚合时会出现精度抖动用 DOUBLE 浪费存储。DECIMAL 是精确类型排序和去重结果都可预期。实际业务中如果只需要城市粒度DECIMAL(7,4) 就够用了但数据包既然给了 6 位小数保留精度没有坏处。city_id 用 INT UNSIGNED 是因为主键不需要负数容量翻倍。AUTO_INCREMENT 属性保证了新增数据的唯一性。budget_level 用 TINYINT 而不是 INT是因为 1 到 5 的枚举值用一字节足够而且 TINYINT 配合注释可以清晰表达业务含义。另一个值得关注的字段是 timezone它存的是 IANA 标准时区标识例如 Asia/Shanghai 这种格式用 VARCHAR(50) 能覆盖最长标识长度比存 UTC 偏移量更利于处理夏令时。4.2 关联表与扩展字段一套完整的迷你数据模型除了城市主表数据包里通常还会附带 1 到 3 张关联表。常见的有景点表、语种表、消费参考表。景点表的典型结构是景点ID、所属城市ID、景点名称、简介、门票参考价。所属城市ID就是外键逻辑上指向城市主表的 city_id。这是一个典型的一对多关系一个城市有多个景点每个景点属于一个城市。CREATE TABLE city_attractions ( attraction_id INT UNSIGNED NOT NULL AUTO_INCREMENT, city_id INT UNSIGNED NOT NULL, attraction_name VARCHAR(150) NOT NULL, intro TEXT, ticket_price DECIMAL(8,2) DEFAULT NULL, PRIMARY KEY (attraction_id), KEY idx_city_id (city_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这段 DDL 描述了景点表可能的结构实际表名以文件为准。city_id 上建了普通索引而不是唯一索引因为一个城市对应多个景点。intro 字段用 TEXT 而不是 VARCHAR原因有两层一是景点描述通常超过 255 字二是 TEXT 类型不占用行内存储空间对 InnoDB 来说超过阈值的内容会存到溢出页行大小更紧凑。ticket_price 用 DECIMAL(8,2)两位小数表示门票价格范围上限 999999.99 足够覆盖任何景点门票。关联表的意义在于这个数据包不只是单表练习而是可以支撑 JOIN 查询的迷你数据模型。实际使用中能从城市表 JOIN 景点表再聚合出“每个城市平均有几个景点”这类指标这套数据才算真正用起来。JOIN 一定要在关联字段上有索引否则 6000 城市 JOIN 几万条景点记录全表嵌套循环能把查询拖到秒级。4.3 数据质量评估值不值得直接用于生产数据是整理过的但整理不等于完美。我建议在深入使用前完成三个质量检查。第一个是空值率检查SELECT COUNT(*) AS total_rows, SUM(CASE WHEN longitude IS NULL THEN 1 ELSE 0 END) AS null_lng, SUM(CASE WHEN latitude IS NULL THEN 1 ELSE 0 END) AS null_lat FROM city_info;如果 null_lng 或 null_lat 比例超过 10%说明大量城市缺失经纬度。这类行在画地图、做距离计算时会直接拖垮效果需要在上游过滤或降级处理。在没有经纬度的城市里做“附近景点”推荐结果一定是错的。第二个是重复检查。城市名在全球范围内存在同名现象直接按英文名去重会把不同地区甚至不同国家的城市误判成重复。所以去重键应该用 city_name_en country 组合SELECT city_name_en, country, COUNT(*) FROM city_info GROUP BY city_name_en, country HAVING COUNT(*) 1;返回结果如果为空说明同名同国家的记录没有重复主键质量合格。如果有结果需要判断是不是同一城市被重复收录比如同一个城市被拼音和英文两种写法各收录一次。这时要保留一条记录并把两边的信息合并。第三个是经纬度范围检查。经度合法范围是 -180 到 180纬度是 -90 到 90。一旦查出超过范围的值说明原始数据有脏数据SELECT city_name_en, longitude, latitude FROM city_info WHERE longitude NOT BETWEEN -180 AND 180 OR latitude NOT BETWEEN -90 AND 90;注意这里用 NOT BETWEEN 来查找异常值。正常数据这条查询应该返回空集一旦有返回这些行的坐标必须修正或剔除。做完这三步你对这份数据的信任度就有了量化依据。有少量空值不意味着不能用可以在查询时用 COALESCE 给默认值或者在应用层做标记。真正不能接受的是重复主键和超范围经纬度这两类错误会直接污染计算结果。5. 避坑指南导入失败、乱码与查询慢的四类典型问题这一章把多次导入类似数据包时踩过的坑整理成清单每条按现象、原因、解决三步走。遇到问题先对照这个清单80% 的情况不需要上网搜。剩下的 20% 属于环境太特殊需要把完整的错误信息贴出来找人讨论。5.1 导入报错 ERROR 1366字符集与严格模式双重夹击现象命令行导入中途出现 ERROR 1366 (HY000): Incorrect string value: \xE6\x97\xA5... for column city_name_cn然后导入中断表里只有部分数据。原因这个错误的字面意思是“字符串值不正确”实际原因通常有两个一是 SQL 文件本身是 UTF-8 编码但导入时客户端字符集不是 utf8mb4导致字节流被错误解释二是字段内容包含 utf8mb4 里的四字节字符常见于 emoji 和生僻字而表定义在导入前已经被建成了 utf8mb3MySQL 8.0 之前叫 utf8或 latin1。utf8mb3 最多只能存三字节的字符遇到 emoji 必然报错。解决先确认文件编码确实是 UTF-8然后在命令行导入时显式指定 --default-character-setutf8mb4。如果是在 Navicat 里运行把连接的“编码”选项和文件的保存编码都改为 UTF-8。如果错误指向某一张表说明这张表的 DDL 里字符集设置不对用 ALTER TABLE 转换-- 将 city_info 表的字符集和排序规则统一为 utf8mb4 ALTER TABLE city_info CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;CONVERT TO 会同时修改表内所有字符串字段的字符集和排序规则并重写已存储的数据。这张表数据量只有几千行执行时间毫秒级。执行后再 SELECT 一次中文应该恢复正常。5.2 外键约束导致导入中断先导主表再导子表现象导入景点关联表时报错 ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails。这张表一条数据都插不进去。原因SQL 文件里同时包含主表和子表的建表与插入语句但执行顺序要么是先建子表再建主表要么是先插子表后插主表。子表里的 city_id 在主表里还不存在外键约束直接拦截。6000 多条数据分成多个批次插入时批次边界也可能触发这个问题。解决三种方案按优先级排列。第一种是调整执行顺序先导入主表数据并验证主键存在再导入子表。第二种是在 mysql 命令行客户端里临时关闭外键检查-- 在 mysql 客户端里执行关闭外键检查后导入 SET FOREIGN_KEY_CHECKS 0; SOURCE /path/to/tourism_cities.sql; SET FOREIGN_KEY_CHECKS 1;SOURCE 是 MySQL 客户端内置命令可以在当前会话里执行 SQL 文件。关闭外键检查后导入导完必须重新做主键存在性校验否则容易留下孤儿数据。第三种最直接导入完成后删除外键指向不存在的行DELETE FROM city_attractions WHERE city_id NOT IN (SELECT city_id FROM city_info);我建议优先用第一种。提前读 SQL 文件的 CREATE TABLE 部分把建表顺序理清楚再执行导入。如果数据包已经规定了导入脚本无法调整顺序再考虑临时关闭外键检查。5.3 查询慢到无法忍受索引缺失与隐式转换现象city_info 表只有 6000 行但执行 WHERE country 某国名 的查询要 200 毫秒以上。对单表几万行的数据来说这个速度明显异常。原因表上没有针对 country 字段的索引。6000 行看起来不多但没有索引时 MySQL 做全表扫描每行都要做字符串比较。更隐蔽的原因是类型不匹配country 列是 VARCHAR查询条件里的值却少了引号MySQL 会把列转换成数值再比较索引直接失效。解决给高频过滤字段加索引。country、continent 这类用于筛选的字段加单列索引即可-- 为高频过滤字段 country 建立单列索引 ALTER TABLE city_info ADD INDEX idx_country (country);加完再跑同样的查询响应时间能降到 10 毫秒以内。对 6000 行的表这个优化收益看起来不大但后续如果做多表 JOIN缺索引的问题会被指数级放大。索引也不是越多越好写频繁的表加太多索引反而拖慢写入按实际查询模式来加。还有一个经验如果 WHERE 条件里同时对 country 和 continent 过滤可以建联合索引 (continent, country)让两个条件都走索引。5.4 中文乱码三层编码必须一致现象SELECT * FROM city_info 返回的结果里中文显示成乱码有时是问号有时是菱形符号 但英文和数字正常。同一条数据在命令行里查是好的在图形化工具里却是乱的。原因客户端、连接、服务器三层字符集不一致。数据表里存的是正确的 UTF-8 字节但客户端工具或连接层用了别的编码来显示。问号出现意味着字符已经在写入时被替换成了 0x3F属于不可逆损坏菱形符号出现说明字节还在只是显示编码不对是可逆的。解决先用以下 SQL 查看当前会话的字符集状态SHOW VARIABLES LIKE character_set%;关注 character_set_client、character_set_connection、character_set_results 三个变量。理想状态是三者都等于 utf8mb4。如果 character_set_client 是 latin1执行SET NAMES utf8mb4;SET NAMES 一条语句同时修改上面三个变量是临时修复最快的方式。客户端工具里的设置路径不同Navicat 在连接属性的“编码”里选 UTF-8命令行连接时加上 --default-character-setutf8mb4 参数。最后要区分可恢复和不可恢复的情况SET NAMES 修好后乱码消失说明数据本身没坏只是显示问题如果执行 SET NAMES 后还是问号说明写入时已经损坏只能从源数据重新导入。6. 进阶用法让 6000 条城市数据跑起来的三个方向导入并验证通过只是开始这份数据真正的价值在于能被业务调用。最后分享三个我常用的落地方向。6.1 经纬度字段的 GIS 化改造数据包里的 longitude 和 latitude 是 DECIMAL 类型能存下来但没法直接做地理距离计算。MySQL 8.0 支持空间数据类型可以把经纬度转换成 POINT 并建立空间索引用 ST_Distance_Sphere 计算两个城市的球面距离-- 新增空间字段 geo并基于经纬度生成 POINT 数据 ALTER TABLE city_info ADD COLUMN geo POINT SRID 4326; UPDATE city_info SET geo ST_SRID(POINT(longitude, latitude), 4326); CREATE SPATIAL INDEX idx_geo ON city_info(geo);空间索引建好后“查离某坐标最近的 10 个城市”就变成索引查询而不是全表扫描。SRID 4326 是 WGS84 坐标系和 GPS 坐标一致不要随意改。这个方向适合要把数据接入地图可视化或做位置推荐的场景。6.2 基于城市表的轻量搜索6000 行的城市名模糊匹配用 LIKE 就行不需要引入搜索引擎。但 LIKE %关键词% 无法利用索引并发一高就会拖慢接口。我一般直接在应用层处理服务启动时把全表加载进内存放入哈希表查询走内存匹配把数据库压力降到最低。这种方案对 6000 条数据的内存开销约几百 KB换来的是接口毫秒级响应性价比很高。如果后续数据量增长到十万级以上再考虑引入成熟的搜索方案不迟。6.3 数据更新的可持续方案数据包是整理好的快照不是实时数据源。把这份数据当成基准表在它之上建立自己的增量更新流程才是正解给城市表增加 update_date 字段每批次写入时记录时间戳合作伙伴的数据变更通过比对 city_name_en country 来合并避免产生重复记录。我的习惯是不直接改动数据包自带的原始表结构而是在它的基础上建视图或扩展表。这样即使哪天拿到了新的数据包要重新导入旧表直接清空重建即可自定义逻辑不受影响。这个习惯帮我躲过不少次数据重导带来的连锁问题希望同样能帮到你。本文还有配套的精品资源点击获取