ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

MySQL零基础实战教程:从安装配置到SQL优化与主从复制

MySQL零基础实战教程:从安装配置到SQL优化与主从复制 1. 先搞清楚MySQL到底是什么零基础该怎么学做后端开发、搞数据分析、干运维或者只是自己想搭个博客、做个毕业设计MySQL基本是绕不开的第一站。它是目前全球使用范围最广的开源关系型数据库任何一个主流语言写的Web项目背后大概率都站着一个MySQL实例。这篇文章是我把自己从零学MySQL到真正上手干活这段路的完整笔记重新整理成一条清晰的学习主线环境安装、表设计、SQL操作、索引、存储过程、事务与锁、性能调优、主从复制再到高频报错排查每一段都是可以直接照着敲的内容。零基础的人跟着顺序做半天时间就能把环境跑起来并完成第一个库表操作已经写过一点SQL的人也能在这里面找到平时容易忽略的坑。先说我不是什么数据库专家就是个被项目逼着从只会SELECT *一路摸过来的普通开发者。所以这篇不谈深奥的源码实现只讲一个原则以真实操作为导向把每个环节拆开揉碎。比如装完MySQL之后到底要改哪些配置才能远程连接比如为什么你的SQL有时候明明有索引却快不起来再比如主从复制到底是怎么一点点配出来的——这些才是日常开发里真正卡人的地方。顺便说一下我的学习建议别一上来就抱着《高性能MySQL》啃先动手把这篇文章里的命令全部跑一遍对数据库建立起原来就这么回事的感觉再回头看书效率会高很多。工具方面准备好一个命令行终端、一个图形化客户端Navicat或DBeaver都行再加上一本地道的参考手册就够了。下面正式开整。2. 环境准备Windows、Linux、Docker三种方式装好MySQL学习MySQL的第一步是把它跑起来但这一步恰恰劝退了很多人。我见过不下十个新手卡在安装上报错五花八门其实多数问题都出在没有理解安装过程中的几个关键选项。这里我把三种最常见的安装方式都过一遍你按自己机器的情况选一种就行。2.1 Windows下用MSI安装包快速安装Windows用户直接去官网下载MySQL Community Server的MSI安装包。下载时注意区分Debug、ZIP Archive和MSI Installer日常使用选MSI。双击安装后选择Setup Type时我建议选Server only因为Developer Default会顺带装一堆你可能用不到的东西。走到Configuration这一步有四个关键选项端口默认3306除非本机端口被占否则不要改。Authentication Method选择Use Legacy Authentication还是Use Strong Password Encryption如果是MySQL 8.0版本且以后要连老项目的老驱动建议选Legacy否则选强加密即可这一步选错很容易在后面出现连接报错。设置root密码这个必须记牢。服务名字保持默认勾选Start at System Startup让MySQL开机自启。装完之后验证很简单。打开命令行输入mysql -uroot -p能进入mysql提示符就算成功。如果提示mysql不是内部或外部命令说明bin目录没加进PATH把C:\Program Files\MySQL\MySQL Server 8.0\bin加进去再重开终端就行。2.2 Linux环境安装与初始密码处理Linux下最常见的坑就是初始密码。在CentOS或RHEL系列上用yum安装后登录时会要求输入初始密码这个密码不会显示在屏幕上需要去日志里翻sudo yum install -y mysql-server sudo systemctl start mysqld sudo grep temporary password /var/log/mysqld.log日志里那串临时密码复制出来登录后第一件事就是改密码ALTER USER rootlocalhost IDENTIFIED BY YourStrongPass123!;注意MySQL 8默认开了密码强度校验插件密码太简单会报错。如果只是学习用想降低校验强度可以执行SET GLOBAL validate_password.policy LOW;Ubuntu系略有不同安装时用apt install mysql-server默认root是通过auth_socket插件认证的也就是你在终端里sudo mysql就能直接进但用密码登录反而进不去。如果想让root支持密码登录执行ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY yourpassword; FLUSH PRIVILEGES;2.3 Docker一分钟拉起MySQL想最快速度体验MySQL或者不想污染本机环境Docker是首选。一条命令搞定docker run -d \ --name mysql8 \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORD123456 \ -e MYSQL_DATABASEtestdb \ -v /data/mysql:/var/lib/mysql \ mysql:8.0这里有几个参数值得说清楚。-e MYSQL_ROOT_PASSWORD是设置root密码MYSQL_DATABASE是启动时自动创建一个数据库。-v挂载数据目录极其重要否则容器一删数据全没。进入容器操作docker exec -it mysql8 mysql -uroot -p如果用Docker DesktopmacOS或Windows直接在图形界面里点Run跑一个mysql容器也一样注意把端口映射映射好就行。2.4 装完之后必做的三件配置环境装好别急着写SQL先把这三件事做了后面能省一堆事。第一创建一个日常使用的专用账号别总用root。root权限太大生产环境这么干等于裸奔学习时也要养成习惯CREATE USER app_userlocalhost IDENTIFIED BY AppPass123!; CREATE USER app_user% IDENTIFIED BY AppPass123!; GRANT ALL PRIVILEGES ON testdb.* TO app_userlocalhost; GRANT ALL PRIVILEGES ON testdb.* TO app_user%; FLUSH PRIVILEGES;第二确认字符集。MySQL 8默认就是utf8mb4但老版本的默认是latin1中文容易乱码。执行SHOW VARIABLES LIKE character_set_server;不是utf8mb4的话在my.cnf的[mysqld]段加上character-set-serverutf8mb4 collation-serverutf8mb4_general_ci第三如果要用图形客户端远程连确认服务端监听了非本地地址。Linux下编辑my.cnf把bind-address从127.0.0.1改成0.0.0.0后重启mysql。改完用netstat -tlnp | grep 3306确认。注意远程连接之前先检查防火墙和云安全组是否放行了3306端口。有学员找我排查了半天连接超时最后发现是云服务器安全组没开端口。3. 基础操作数据库、表设计与增删改查全掌握环境OK之后进入真正的核心SQL操作。这一章内容多但都是每天要用的基本功建议每个命令都亲手跑一遍。3.1 数据库与表的创建、查看、删除先创建一个学习用的库名字叫schoolCREATE DATABASE school DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; SHOW DATABASES; USE school;COLLATE utf8mb4_general_ci是排序规则这个设置会让后续表继承相同的字符集避免中英文混排时出现乱码。查看当前库下所有表用SHOW TABLES;删除整个库用DROP DATABASE school;这条命令很危险会把库连同里面的数据一起删掉没有确认提示。3.2 数据类型选择初学者最该认真选的一步很多新手建表时一股脑全用VARCHAR或TEXT这是大忌。什么类型存什么数据不只是省空间的问题还直接影响查询速度和SQL写法。我的选择经验整数用INT或BIGINT主键用INT UNSIGNED AUTO_INCREMENT。注意INT最大21亿用户表、订单表这种增长快的主键建议直接上BIGINT。金额用DECIMAL(10,2)绝对不要用FLOAT或DOUBLE浮点数会有精度问题账面对不上就哭吧。短文本用VARCHAR(50)到VARCHAR(255)特别长的内容用TEXT。日期用DATETIME或TIMESTAMP。TIMESTAMP会自动存储UTC并按会话时区转换DATETIME存什么就是什么国内项目我习惯用DATETIME省得处理时区差异。状态类字段别用字符串直接用TINYINT配合注释说明含义。布尔类型在MySQL里就是TINYINT(1)写is_deleted TINYINT NOT NULL DEFAULT 0。3.3 建表、约束与默认值设置结合经典的学生-课程-成绩场景我建一张学生表把常见的约束一次讲清楚CREATE TABLE student ( id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY COMMENT 主键, student_no VARCHAR(20) NOT NULL UNIQUE COMMENT 学号唯一, name VARCHAR(50) NOT NULL COMMENT 姓名, gender TINYINT NOT NULL DEFAULT 0 COMMENT 0未知 1男 2女, age TINYINT UNSIGNED DEFAULT NULL COMMENT 年龄, class_id INT UNSIGNED NOT NULL DEFAULT 0 COMMENT 班级ID默认0表示未分配, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 更新时间 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT学生表;这里面几个点值得展开。NOT NULL DEFAULT 0就是热搜里那个mysql设置默认值为0的用法当不确定某记录该填什么时用默认值兜底比存NULL更好因为NULL参与计算时会污染结果。ON UPDATE CURRENT_TIMESTAMP会在每次更新行时自动刷新时间省得业务代码手动维护。UNIQUE KEY用来保证学号不重复。真正要防并发重复插入时唯一约束比先SELECT再INSERT靠谱得多。3.4 增删改查精讲与经典踩坑点插入数据单条、批量一起讲INSERT INTO student (student_no, name, gender, age, class_id) VALUES (2024001, 张三, 1, 18, 1); INSERT INTO student (student_no, name, gender, age, class_id) VALUES (2024002, 李四, 2, 19, 1), (2024003, 王五, 1, 20, 2), (2024004, 赵六, 2, NULL, 2);批量插入是提升写入性能最直接的手段能合并多条INSERT就合并。带主键冲突时想要有则更新无则插入用ON DUPLICATE KEY UPDATEINSERT INTO student (student_no, name, age) VALUES (2024001, 张三, 19) ON DUPLICATE KEY UPDATE name VALUES(name), age VALUES(age);UPDATE和DELETE是事故高发区。最常见的错误就是忘了加WHERE一条UPDATE把全表数据改了这种事故几乎每个团队都发生过。写更新之前先看一眼能不能用相同条件SELECT出预期行数UPDATE student SET age 19 WHERE student_no 2024001; DELETE FROM student WHERE student_no 2024004;DELETE是逐行删除会走事务可以回滚。如果你的目的是清空整表数据用TRUNCATE TABLE student;它直接重建表速度快得多但不可回滚且如果有外键引用会失败。查询是重中之重这里给一个综合示例把查询的关键子句都串起来SELECT class_id, COUNT(*) AS total, AVG(age) AS avg_age, MAX(age) AS max_age FROM student WHERE age IS NOT NULL AND class_id IN (1, 2) GROUP BY class_id HAVING COUNT(*) 1 ORDER BY total DESC LIMIT 10;执行顺序要先心里有数FROM是最先的接着是WHERE过滤然后GROUP BY分组HAVING过滤分组SELECT投影ORDER BY排序最后LIMIT分页。搞清楚这个顺序很多奇怪的SQL结果都能解释得通。3.5 排序、分页与聚合函数的实用技巧ORDER BY支持多列排序比如先按班级升序班内按年龄降序SELECT * FROM student ORDER BY class_id ASC, age DESC;中文排序是个容易被忽略的坑。默认的utf8mb4_general_ci对中文拼音的排序是不符合直觉的如果你有姓名按拼音排的需求在排序时指定排序规则SELECT name FROM student ORDER BY name COLLATE utf8mb4_unicode_ci;分页查询在数据量大时要警惕LIMIT的偏移陷阱。MySQL先查出偏移量之前的所有数据再丢弃偏移越大越慢。比如LIMIT 100000, 20要扫描10万行性能惨不忍睹。优化方式是改写成基于上一页最大ID的查询SELECT * FROM student WHERE id 100000 ORDER BY id ASC LIMIT 20;这种方案只走索引数据量再大也扛得住。聚合函数方面COUNT(*)和COUNT(1)现代版本性能差别微乎其微但COUNT(字段)会忽略该字段为NULL的行统计逻辑要想清楚。4. 进阶技能索引、视图、存储过程与触发器基础SQL熟练之后就该碰碰能真正提升开发效率和查询性能的东西了。这一章的内容在工作中使用频率极高面试也常考。4.1 索引为什么加了索引查询就变快索引的本质是额外的有序数据结构MySQL的InnoDB引擎使用的是B树。你可以把它想象成书的目录没有目录时找内容只能一页页翻全表扫描有目录先定位到章节再翻到具体页快得多。创建索引的语法很简洁CREATE INDEX idx_class_id ON student(class_id); CREATE UNIQUE INDEX idx_student_no ON student(student_no);但索引不是随便建的。几个必须记住的核心点联合索引遵循最左前缀原则。比如建了(class_id, age)联合索引查询条件里只有class_id时能用到索引只有age时用不到。对索引列做函数运算、隐式类型转换、前导模糊查询都会让索引失效。比如WHERE name LIKE %张%走不了索引但WHERE name LIKE 张%可以。索引不是越多越好每个索引都会拖慢写入速度、占用磁盘空间。一张表建议控制在5个以内把索引留给高频查询列。想确认SQL到底用没用到索引用EXPLAINEXPLAIN SELECT * FROM student WHERE student_no 2024001;看type列从高到低是system const eq_ref ref range index ALL。只要不是ALL说明至少走了一部分索引。再看key列显示实际用到的索引名。4.2 视图把复杂查询封装成虚拟表视图就是一个保存好的查询用的时候当表一样查。比如把学生班级成绩三表关联的查询封装成视图CREATE VIEW v_student_score AS SELECT s.student_no, s.name, c.class_name, sc.score FROM student s JOIN class c ON s.class_id c.id LEFT JOIN score sc ON sc.student_id s.id;之后业务代码里直接SELECT * FROM v_student_score WHERE score 85;就行不用每次写冗长的JOIN。视图的好处一是简化调用二是隐藏底层表结构细节三是可以用它限制敏感字段的暴露。但视图也有坑基于多表JOIN的视图不能直接更新普通视图更新本质是更新基表很多开发者以为更新视图就能更新数据结果发现某些列更新不了还报错。视图适合读场景别把写逻辑也压在视图上。4.3 存储过程一段能复用的事务性代码存储过程就是把一段SQL逻辑预先编译存放在数据库里应用层用一句CALL就能调用。它特别适合执行批量报表、复杂业务计算这类逻辑。声明一个根据班级ID统计人数的存储过程DELIMITER $$ CREATE PROCEDURE count_student_by_class(IN p_class_id INT, OUT p_total INT) BEGIN SELECT COUNT(*) INTO p_total FROM student WHERE class_id p_class_id; END$$ DELIMITER ;这里DELIMITER是新手最容易疑惑的地方。MySQL默认用分号作为语句结束符但存储过程内部有大量分号为了让MySQL知道整个CREATE PROCEDURE是一个完整语句你得先把终止符临时改成$$定义完再改回来。忘写DELIMITER或者改完没恢复都会出现莫名其妙的语法报错。调用方式CALL count_student_by_class(1, total); SELECT total;total是用户变量存储过程执行完可以通过它拿到输出参数的值。存储过程的输入参数用IN输出参数用OUT既能进又能出的用INOUT。调试存储过程比较痛苦建议在过程中用临时表记录中间结果或者分段注释定位问题别指望一步到位。4.4 触发器与批处理场景触发器是在表的INSERT、UPDATE、DELETE事件发生时自动执行的逻辑。比如给student表加一个操作日志表每次新增或修改学生时自动记录CREATE TABLE student_log ( id INT AUTO_INCREMENT PRIMARY KEY, student_id INT NOT NULL, action VARCHAR(20) NOT NULL, log_time DATETIME DEFAULT CURRENT_TIMESTAMP ); DELIMITER $$ CREATE TRIGGER trg_student_insert AFTER INSERT ON student FOR EACH ROW BEGIN INSERT INTO student_log(student_id, action) VALUES (NEW.id, INSERT); END$$ DELIMITER ;NEW表示新插入的行OLD表示被修改/删除前的行比如UPDATE触发器里可以用OLD.age和NEW.age对比出变化。触发器虽方便我个人的建议是能不用就不用。隐式逻辑太强会导致业务行为变得难追踪数据库层报错了也难排查尤其是数据迁移、批量导入时一个触发器可能会让性能断崖下跌也会导致主从复制出奇奇怪怪的问题。如果你只是想维护updated_at这类时间字段用默认的ON UPDATE CURRENT_TIMESTAMP就够了别动不动上触发器。5. 事务与锁并发场景里保命的基础数据库一旦被多个请求并发访问事务和锁就是绕不开的话题。这块既是日常开发的痛点也是面试的高频考点。5.1 事务的ACID与基本用法一个事务是把多条SQL放进同一个执行单元要么全部成功要么全部回滚。经典场景就是转账扣钱和加钱必须在一个事务里否则钱就对不上。START TRANSACTION; UPDATE account SET balance balance - 100 WHERE user_id 1; UPDATE account SET balance balance 100 WHERE user_id 2; COMMIT;任何一条UPDATE报错执行ROLLBACK;就能撤销整个事务。ACID四个特性值得花五分钟理解原子性Atomicity保证事务不可分割一致性Consistency保证事务前后数据总满足约束隔离性Isolation保证并发事务互不干扰持久性Durability保证提交后的数据不会丢失。事务之间默认不能完全隔离所以才有隔离级别。5.2 隔离级别与脏读、幻读、不可重复读MySQL提供了四个隔离级别隔离级别脏读不可重复读幻读READ UNCOMMITTED会会会READ COMMITTED否会会REPEATABLE READ默认否否会InnoDB可避免SERIALIZABLE否否否设置方式SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;三个读背后的坑我举个例子。脏读是读到别人事务未提交的数据别人回滚了你就读了个假数据。不可重复读是同一事务内两次读同一行结果因为别人提交而不同。幻读是同一事务内两次范围查询行数不一样了像变魔术一样多出一行。MySQL默认是REPEATABLE READ并且通过MVCC多版本并发控制和Next-Key Lock在大多数场景下把幻读问题规避掉了。实际开发中生产系统用READ COMMITTED也很常见因为RR在部分复杂场景下会有更多锁开销。锁的机制不必深钻到源码但要知道每个隔离级别能解决什么、防不住什么。5.3 锁的类型与锁表事故排查MySQL的锁从粒度上分表锁和行锁。MyISAM引擎只用表锁并发写性能差你现在建表用的InnoDB引擎支持行级锁并发能力好得多。行锁里有个容易踩坑的间隙锁Gap Lock。在RR隔离级别下给一个范围加锁时InnoDB不仅锁住存在的行还会锁住范围内的空隙防止其他事务往这个范围插入新行。这本来是为了防幻读但也导致了一个经典线上事故事务A按范围更新一批数据没提交事务B在这个范围内插入新行被卡死死等。你搜索记录里的mysql锁表八成就是指这种。排查步骤SHOW PROCESSLIST;看有没有大量Sleep状态、或处于Waiting for lock的连接。定位到阻塞源头后再查询锁等待信息SELECT * FROM performance_schema.data_lock_waits\G SELECT * FROM sys.innodb_lock_waits\G找出持有锁的事务要么让它尽快提交要么直接KILL对应线程ID。预防手段其实很朴素事务尽量短小更新操作用索引命中行避免在无索引条件下执行UPDATE导致行锁升级为表锁业务高峰期不要跑长时间的大事务。6. 性能调优与主从复制从能跑到扛得住写了几年代码我终于意识到SQL写出来能出结果只是及格线线上真的被压出问题来调优能力才是分水岭。这一章讲几个最实用、也最能救命的优化手段。6.1 慢查询日志与EXPLAIN分析排查性能问题的第一步永远是把慢的SQL找出来。开启慢查询日志超过阈值的SQL会被记录下来SET GLOBAL slow_query_log ON; SET GLOBAL long_query_time 1;日志默认写在数据目录里看路径用SHOW VARIABLES LIKE slow_query_log_file;。此时故意跑一条大表全表扫描的查询再去日志里看就能看到执行耗时、扫描行数。定位到慢SQL后上EXPLAIN分析执行计划。重点关注几个列type达到range以上才算合格ALL需要警惕。key是否使用了预期索引。rows预估扫描行数数量差距大说明数据统计信息过期用ANALYZE TABLE student;更新。Extra看到Using temporary、Using filesort说明分组排序在临时表中进行量大时很慢通常需要优化索引。有个很典型的优化案例原始SQL是SELECT * FROM orders WHERE user_id 1001 ORDER BY create_time DESC LIMIT 10;表量大之后排序很慢。优化方式是建联合索引(user_id, create_time)这样排序就能直接走索引省掉filesort查询时间从几百毫秒降到几毫秒。6.2 连接池应用端不容忽视的配置数据库连接是有成本的每建立一个MySQL连接都要经过TCP握手、认证。如果每次请求都新建连接并发上来必然拖垮数据库。所以应用层普遍使用连接池把连接复用起来。以Java的HikariCP为例核心参数就几个maximum-pool-size: 20 minimum-idle: 5 connection-timeout: 30000 max-lifetime: 1800000maximum-pool-size是池子的最大连接数很多人误以为越大越好其实MySQL默认最大连接数是151连接数设太大反而造成资源竞争。经验公式是核心CPU核心数的两三倍结合压测结果调整。Python里使用PyMySQL时配合DBUtils的PooledDB做连接池也是同理如果你直接用pymysql.connect每次请求创建一个连接并发一到就等着超时吧。6.3 主从复制原理、配置与常见问题怎么使用mysql主从复制是很多人升职加薪的第一道坎。主从复制的核心原理其实很朴素主库把数据变更写入binlog二进制日志从库拉取binlog并重放实现数据同步。逻辑链条是主库执行SQL-binlog记录-从库IO线程拉取-写入relay log-从库SQL线程重放。配置主库在my.cnf里加[mysqld] server-id1 log-binmysql-bin binlog-do-dbschool重启MySQL然后创建复制专用账号CREATE USER repl% IDENTIFIED BY ReplPass123!; GRANT REPLICATION SLAVE ON *.* TO repl%; FLUSH PRIVILEGES; SHOW MASTER STATUS;记下File和Position两列比如mysql-bin.000001和154。配置从库my.cnf加上[mysqld] server-id2 relay-logrelay-bin重启后执行CHANGE MASTER TO MASTER_HOST主库IP, MASTER_USERrepl, MASTER_PASSWORDReplPass123!, MASTER_LOG_FILEmysql-bin.000001, MASTER_LOG_POS154; START SLAVE; SHOW SLAVE STATUS\G重点看两个字段Slave_IO_Running: Yes和Slave_SQL_Running: Yes。都显示Yes才算同步正常。常见的坑有server-id重复导致连接失败主从数据库名不一致MASTER_LOG_POS填错导致从库找不到日志位置这些都需要回头逐项核对。6.4 把远程库的某张表同步到本地的完整操作方法热搜词里正好有一条把远程库的这张表同步到本地这个需求实际工作中太常碰到了——测试环境需要一份生产数据、本地开发要联调、或者要迁移一张业务表。最容易上手的方案是用mysqldump精确导出指定表。比如远程库的user表只想同步数据不全量拉mysqldump -h 远程IP -P 3306 -u user -p --single-transaction \ --default-character-setutf8mb4 \ dbname user --whereid 1000 user_dump.sql然后本地导入mysql -uroot -p -hlocalhost dbname user_dump.sql如果目标是持续同步某张表而不是一次性拷贝那就该上主从复制并把从库的replicate-do-table配置限制为这张表[mysqld] replicate-do-tableschool.student这样只同步指定表其他表不去碰负担小也安全。还有一种场景是两边都在写入同一张表想双向同步——这种需求千万别用原生主从直接考虑数据同步中间件或者从架构上避免双向写。7. 常见问题排查速查手册我踩过的那些坑教程的最后我把搜索量最高的几个MySQL报错和疑难场景集中整理成一份排查手册。这些不是我凭空编的全是真实被问过、真实踩过的坑。7.1 ERROR 2002 (HY000): Cant connect to local MySQL server through socket这个报错几乎每个Linux新人都会碰到。报错字面意思是通过socket文件连不上本地的MySQL服务最常见的原因就是MySQL服务根本没启动。先冷静执行systemctl status mysqld如果显示inactive直接systemctl start mysqld再试。如果服务确实在运行还报这个错可能是socket文件路径不一致。MySQL默认的socket路径通常在/var/lib/mysql/mysql.sock但客户端却去/tmp/mysql.sock找。绕开socket的方式是强制走TCPmysql -uroot -p -h 127.0.0.1 -P 3306如果这样能连上说明就是socket路径问题。在my.cnf的[mysqld]和[client]两个段都加上socket/var/lib/mysql/mysql.sock保持统一即可。一句话总结报这个错先确认服务活着再看socket路径别一上来就重装。7.2 SSL连接错误与时区报错连接MySQL 8时常见的两个报错一个是SSL相关的SSL connection error另一个是时区相关的serverTimezone异常。原因是在MySQL 8.0中服务端默认启用了SSL相关认证如果客户端驱动版本旧或配置不一致就会握手失败。Java JDBC连接串这样处理jdbc:mysql://127.0.0.1:3306/school?useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltruecharacterEncodingutf8serverTimezoneAsia/Shanghai解决的是8.0驱动默认要求设置时区的问题。allowPublicKeyRetrievaltrue是为了使用caching_sha2_password认证方式时允许客户端从服务端获取公钥。Python端连接时也有相似情况PyMySQL用ssl_disabledTrue来跳过SSLimport pymysql conn pymysql.connect( host127.0.0.1, useruser, passwordpwd, databaseschool, charsetutf8mb4, ssl_disabledTrue )7.3 忘记MySQL密码与查看初始密码忘记root密码是所有人都经历过的尴尬。通用的救援手段是先用skip-grant-tables跳过权限校验启动服务。Linux下的操作systemctl stop mysqld mysqld_safe --skip-grant-tables --skip-networking mysql -uroot进入后直接改密码FLUSH PRIVILEGES; ALTER USER rootlocalhost IDENTIFIED BY NewPass123!;修改完必须重启MySQL恢复正常模式否则你的数据库会一直处于无认证可访问的状态这是非常危险的事。另外注意新版MySQL 8在修改认证信息前可能需要先FLUSH PRIVILEGES否则会报未知错误。CentOS安装后忘了临时密码在哪个日志就回到2.2节用grep命令去找。7.4 中文字符乱码与排序异常乱码问题的排查思路只有一个从客户端、连接层、服务端、库表四个环节确认字符集是否全部统一为utf8mb4。执行这条命令看三处关键变量SHOW VARIABLES LIKE character_set%;重点关注character_set_client、character_set_connection、character_set_results。如果客户端侧不对最简单的方法是登录后先执行SET NAMES utf8mb4;一次性把这三个变量都改对。持久化方案还是在my.cnf里配置默认字符集。数据库里查出来已经是乱码的数据说明写入时就错了改配置只能防止新数据继续乱码已损坏的数据需要用CAST转换或从备份恢复没有银弹。7.5 建表设计的几个收尾提醒最后聊几句表设计。前面3.3节的学生表只建了学生基础信息实际项目中还要配套课程表、成绩表成绩表通常用联合主键或唯一索引防止同一学生同一课程重复记录CREATE TABLE score ( student_id INT UNSIGNED NOT NULL, course_id INT UNSIGNED NOT NULL, score DECIMAL(5,2) NOT NULL DEFAULT 0, exam_date DATE NOT NULL, PRIMARY KEY (student_id, course_id), KEY idx_course (course_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;联合主键的好处是数据库层面就把重复选课成绩防死了。建表时想清楚三件事基本就不会出大错这张表描述的对象是谁主键是谁、哪些字段是绝对必填的NOT NULL、哪些字段要频繁查询建索引。等你建的表和线上需求磨合一轮自然会形成自己的判断。MySQL这个生态太庞大了一篇文章不可能覆盖全部但如果你把前面的内容从头到尾操作一遍日常项目的读写、设计、排障基本就够用了。我个人这几年的体会是数据库的问题十有八九不是不知道某个高级功能而是基本功不扎实——字符集没统一、事务忘了提交、索引建得随意、连接池配置拍脑袋。把最基础的东西做规范比追求各种花哨技巧有用得多。真遇到文章没覆盖到的报错记住一条原则别乱猜去翻SHOW VARIABLES、SHOW STATUS、EXPLAIN和错误日志数据会告诉你答案。
返回列表