戰(zhàn):從原理到踩坑全解析)
MySQL修改密碼和配置文件這兩個(gè)詞放在一起看起來是兩個(gè)獨(dú)立的小操作但實(shí)際工作中它們經(jīng)常被綁在一起處理。比如你剛裝完MySQL第一件事往往就是給root換個(gè)密碼又比如某個(gè)環(huán)境突然連接數(shù)爆了你第一反應(yīng)也是翻配置文件調(diào)max_connections。我這些年幫別人排查過不少數(shù)據(jù)庫問題發(fā)現(xiàn)一大半所謂的數(shù)據(jù)庫連不上密碼過期端口被占最后都繞回到這兩件事上要么密碼根本沒改對要么配置文件壓根沒生效。這篇就基于我自己的實(shí)操經(jīng)驗(yàn)把修改密碼和配置文件這兩塊掰開揉碎講清楚從原理到命令、從踩坑到排查給你一條能直接照著走的完整路線。不管你是剛?cè)胄械暮蠖碎_發(fā)、運(yùn)維新人還是自己折騰服務(wù)器的小白這篇文章都能讓你少走幾趟彎路。我不會堆一堆無聊的參數(shù)表而是把那些真正決定成敗的細(xì)節(jié)、那些官方文檔里不會寫的坑全都給你翻出來。1. 修改密碼前必須先搞懂的幾個(gè)底層概念1.1 密碼到底存在哪兒改的是哪張表很多新手以為MySQL的密碼是個(gè)獨(dú)立的密鑰文件其實(shí)不是MySQL把用戶信息和密碼哈希都放在一個(gè)叫mysql.user的系統(tǒng)表里。當(dāng)你執(zhí)行改密命令時(shí)本質(zhì)上是更新這張表里對應(yīng)User和Host字段的authentication_string字段。這個(gè)Host字段特別關(guān)鍵很多人改密碼后死活連不上就是因?yàn)闆]注意到同一個(gè)用戶名可能對應(yīng)了好幾個(gè)Host記錄。舉個(gè)例子你執(zhí)行ALTER USER rootlocalhost IDENTIFIED BY newpass改的只是rootlocalhost這條記錄但如果你的客戶端是從其他機(jī)器連過來的數(shù)據(jù)庫匹配的可能是root%這條記錄。這時(shí)候你改完密碼自己本地連上去沒問題但程序遠(yuǎn)程一連就報(bào)Access denied然后你開始懷疑人生。所以要養(yǎng)成習(xí)慣改密碼之前先查一下SELECT user, host, authentication_string FROM mysql.user WHERE user root;把這條記錄里所有的Host變體都看一遍再決定改哪條。這說明一個(gè)道理MySQL的權(quán)限體系是按用戶名來源主機(jī)這個(gè)組合來區(qū)分用戶的同一個(gè)用戶名在不同Host下完全是兩個(gè)獨(dú)立賬號。1.2 密碼哈希的演進(jìn)與兼容性坑MySQL 5.7及之前版本默認(rèn)用mysql_native_password插件密碼的哈希值是40位十六進(jìn)制字符串。MySQL 8.0開始默認(rèn)換成了caching_sha2_password哈希串變成了64位而且認(rèn)證過程中的處理邏輯完全不同。這個(gè)改動帶來了一個(gè)非常常見的兼容性問題你拿著MySQL 8.0建的用戶用老版本的客戶端驅(qū)動去連會報(bào)Authentication plugin caching_sha2_password cannot be loaded。我在實(shí)際項(xiàng)目中遇到過很多次這種問題最典型的是老項(xiàng)目用的jar包連接MySQL 8.0數(shù)據(jù)庫。解決方案有兩個(gè)要么升級驅(qū)動要么在建用戶或改密碼時(shí)指定用老認(rèn)證插件ALTER USER user% IDENTIFIED WITH mysql_native_password BY password;如果只是臨時(shí)讓某個(gè)賬號兼容舊客戶端這是個(gè)很快的解法。不過從長期來看還是建議升級驅(qū)動畢竟老插件早晚會被徹底移除。另外要注意mysql_native_password在MySQL 8.0里默認(rèn)是禁用插件狀態(tài)直接用IDENTIFIED WITH指定它有時(shí)候會報(bào)錯(cuò)需要先在配置文件里加上default_authentication_pluginmysql_native_password或通過安裝插件的方式啟用。各版本差異我整理成了下面這個(gè)表格版本默認(rèn)認(rèn)證插件密碼哈希長度常見坑5.6mysql_native_password40位基本穩(wěn)定5.7mysql_native_password40位無顯著坑8.0caching_sha2_password64位老客戶端連不上8.4caching_sha2_password64位部分老插件被移除1.3 你執(zhí)行改密命令之后MySQL內(nèi)部發(fā)生了什么了解這一步會幫你理解為什么有時(shí)候命令都成功了但密碼還是沒變。當(dāng)你執(zhí)行ALTER USER語句時(shí)MySQL服務(wù)端會校驗(yàn)當(dāng)前登錄用戶是否有CREATE USER權(quán)限或足夠的管理權(quán)限解析你指定的認(rèn)證插件和明文密碼把明文密碼通過選定插件生成對應(yīng)的哈希摘要更新mysql.user表對應(yīng)記錄觸發(fā)內(nèi)存中權(quán)限緩存刷新注意第5步這是個(gè)大坑。如果會話的權(quán)限表緩存沒有刷新某些場景下可能仍然使用舊密碼完成認(rèn)證。實(shí)際上ALTER USER內(nèi)部會隱式刷新但如果是直接UPDATE mysql.user改哈希這個(gè)操作我不建議除非你知道自己在干什么就必須手動執(zhí)行FLUSH PRIVILEGES。很多老教程會教人直接UPDATE user SET passwordPASSWORD(...),在8.0里PASSWORD()函數(shù)都沒了這條路徹底走不通。2. 配置文件才是真正的幕后黑手2.1 配置文件的優(yōu)先級和加載順序MySQL配置文件在Linux上通常叫my.cnf在Windows上叫my.ini。但同一臺機(jī)器上完全可能存在多個(gè)同名配置文件它們之間有嚴(yán)格的優(yōu)先級順序。啟動時(shí)MySQL會按照這個(gè)順序讀取配置后讀取的會覆蓋先讀取的重復(fù)項(xiàng)/etc/my.cnf/etc/mysql/my.cnf$MYSQL_HOME/my.cnf~/.my.cnf搞清楚你當(dāng)前實(shí)例到底用的是哪個(gè)文件是我見過無數(shù)人踩坑的第一道坎。你以為改了/etc/my.cnf但實(shí)際MySQL可能因?yàn)槟硞€(gè)deb包安裝路徑的關(guān)系默認(rèn)加載的是/etc/mysql/mysql.conf.d/mysqld.cnf。更折騰的是你在命令行啟動時(shí)還能直接指定--defaults-file/path/to/my.cnf如果之前用這個(gè)參數(shù)啟動過實(shí)例那前面所有默認(rèn)路徑就統(tǒng)統(tǒng)失效只認(rèn)你指定那一個(gè)。判斷當(dāng)前實(shí)例加載了哪個(gè)配置文件最簡單的辦法是登錄MySQL執(zhí)行SHOW VARIABLES LIKE basedir; SHOW VARIABLES LIKE datadir;或者直接看啟動進(jìn)程ps aux | grep mysqld用這種方式能看到啟動命令里有沒有帶--defaults-file參數(shù)。我建議一上來就養(yǎng)成習(xí)慣用my_print_defaults mysqld命令查看當(dāng)前環(huán)境下mysqld會讀取的配置項(xiàng)my_print_defaults mysqld這個(gè)命令會直接輸出配置項(xiàng)生效后的結(jié)果能幫你快速確認(rèn)哪些參數(shù)是從哪個(gè)文件來的。2.2 分組和參數(shù)生效規(guī)則配置文件里的內(nèi)容不是隨便平的它分了好幾個(gè)段section比如[client]、[mysql]、[mysqld]、[mysqldump]。不同程序啟動時(shí)只讀取對應(yīng)段下的參數(shù)。這個(gè)設(shè)計(jì)經(jīng)常讓人鬧笑話有人把port3306寫在[mysql]段客戶端連的時(shí)候倒是能讀到端口但mysqld服務(wù)端根本不認(rèn)這個(gè)段的參數(shù)服務(wù)還是跑的默認(rèn)3306。[mysqld]是服務(wù)端核心段改端口、數(shù)據(jù)目錄、字符集、連接數(shù)這些都要放在這里。[client]段主要給命令行客戶端用比如你可以統(tǒng)一寫userroot省得每次敲命令還得指定用戶。[mysql]段針對交互式命令行客戶端。還有[mysqldump]段可以配置導(dǎo)出的默認(rèn)字符集。搞清楚這些區(qū)別你就能猜到為什么有時(shí)候配置看著對了實(shí)際卻不生效。另外一個(gè)需要知道的概念是并非所有參數(shù)都能動態(tài)修改有些參數(shù)必須在配置文件中設(shè)置并重啟實(shí)例才能生效。MySQL把參數(shù)分成兩類動態(tài)參數(shù)可以用SET GLOBAL實(shí)時(shí)改靜態(tài)參數(shù)只能改配置文件重啟。比如port就是靜態(tài)參數(shù)你在數(shù)據(jù)庫里執(zhí)行SET GLOBAL port3307會直接報(bào)錯(cuò)。再比如max_connections是動態(tài)參數(shù)你可以先在線調(diào)大救急但要永久生效還是得寫進(jìn)配置文件。2.3 常見核心參數(shù)到底該怎么配配置文件里參數(shù)多如牛毛真正日常高頻會用到的核心參數(shù)其實(shí)就那些。我給出一套基于通用場景的推薦模板注意這是基于常見實(shí)踐的推薦實(shí)際取值要看機(jī)器配置[mysqld] port3306 datadir/var/lib/mysql socket/var/lib/mysql/mysql.sock max_connections500 max_connect_errors1000 character_set_serverutf8mb4 collation_serverutf8mb4_unicode_ci innodb_buffer_pool_size2G innodb_flush_log_at_trx_commit1 slow_query_log1 slow_query_log_file/var/log/mysql/slow.log long_query_time2max_connections這個(gè)值不是越大越好。每建一個(gè)連接MySQL都要給線程棧、臨時(shí)表分配內(nèi)存。如果機(jī)器只有4G內(nèi)存卻把連接數(shù)拉到2000一旦真來2000個(gè)并發(fā)連接內(nèi)存瞬間被吃掉一大半反而拖垮數(shù)據(jù)庫性能。通常我按(可用內(nèi)存 - 系統(tǒng)預(yù)留 - InnoDB緩沖池大小) / 單連接內(nèi)存開銷來估算單個(gè)連接內(nèi)存開銷大約在幾百KB到1MB之間壓力測試的時(shí)候要專門驗(yàn)證。innodb_buffer_pool_size是InnoDB引擎的緩存池用來緩存數(shù)據(jù)頁和索引頁。原則上在總內(nèi)存里給實(shí)例預(yù)留出操作系統(tǒng)和其他服務(wù)的空間后這個(gè)值可以占到內(nèi)存的70%左右。但如果你的機(jī)器上還跑了別的服務(wù)就要克制一點(diǎn)別把內(nèi)存全吃干凈。character_set_server和collation_server是字符集和排序規(guī)則強(qiáng)烈建議從初始化就固定成utf8mb4不要用utf8。為什么因?yàn)镸ySQL的utf8其實(shí)是utf8mb3它最多只支持3字節(jié)字符像emoji這種4字節(jié)字符會直接存不進(jìn)去或者變成亂碼。你用utf8mb4不僅完全兼容utf8還能多支持emoji和生僻字沒有理由不用它。3. 完整實(shí)操從忘記root密碼到配置調(diào)優(yōu)3.1 模擬項(xiàng)目環(huán)境準(zhǔn)備為了讓你有代入感我拿一個(gè)典型的模擬項(xiàng)目環(huán)境來說事。某公司的運(yùn)營后臺系統(tǒng)用的是MySQL 5.7部署在一臺Linux服務(wù)器上數(shù)據(jù)庫除了本地程序訪問還允許運(yùn)維網(wǎng)段的某臺跳板機(jī)遠(yuǎn)程連接。前幾天工作人員告訴我root密碼找不到了而且程序連接數(shù)一直告警。接到這個(gè)任務(wù)我腦子里已經(jīng)有了完整操作路徑。首先要做的不是摸黑干活而是先確認(rèn)版本、配置文件路徑和啟動方式mysql --version ps aux | grep mysqld ls -l /etc/my.cnf /etc/mysql/my.cnf確認(rèn)版本是5.7進(jìn)程啟動命令是mysqld --defaults-file/etc/my.cnf,這就說明生效的配置文件大概率是/etc/my.cnf。確認(rèn)這些之后再開始處理密碼問題。3.2 場景A忘記root密碼的應(yīng)急修改忘記root密碼是每個(gè)DBA和全棧工程師都會遇到的經(jīng)典場景。如果是內(nèi)部開發(fā)環(huán)境最常規(guī)的就是通過跳過權(quán)限認(rèn)證的方式啟動MySQL進(jìn)去之后重新設(shè)置密碼。詳細(xì)步驟如下第一步先停掉MySQL服務(wù)。不同系統(tǒng)的服務(wù)名不一樣Debian系一般是mysqlCentOS系一般是mysqldsystemctl stop mysqld如果你是在容器里跑的就暫停容器或者直接停掉進(jìn)程。第二步修改配置文件在[mysqld]段下加上這么一行skip-grant-tables這一步的作用是讓MySQL在啟動時(shí)跳過所有權(quán)限驗(yàn)證任何人連上來都是超級管理員所以操作要快且僅限于本機(jī)臨時(shí)方案絕對不能暴露到外網(wǎng)。第三步啟動MySQL服務(wù)然后無密碼登錄systemctl start mysqld mysql -u root如果連不進(jìn)去檢查一下socket文件路徑是否正?;蛘哂胢ysql -u root -h 127.0.0.1配合TCP方式連接。第四步登錄后執(zhí)行改密。在MySQL 5.7里因?yàn)槟J(rèn)還可以用mysql_native_password可以用FLUSH PRIVILEGES; ALTER USER rootlocalhost IDENTIFIED BY NewStrongPass123;注意這里為什么必須先FLUSH PRIVILEGES因?yàn)樵趕kip-grant-tables模式下MySQL不加載權(quán)限表直接ALTER USER可能會報(bào)錯(cuò)或者不生效先刷新權(quán)限讓MySQL重新讀取權(quán)限表后面操作才穩(wěn)。也有直接先UPDATE mysql.user SET authentication_stringPASSWORD(...)的寫法但PASSWORD()函數(shù)在新版本已經(jīng)廢棄所以要么用ALTER USER要么在5.7里用SET PASSWORD FOR rootlocalhostPASSWORD(...)而在8.0里就統(tǒng)一用ALTER USER。第五步千萬記得把配置文件里加的skip-grant-tables注釋掉或刪掉然后重啟MySQLsystemctl restart mysqld mysql -u root -p這一步很多人會忘導(dǎo)致數(shù)據(jù)庫一直裸奔在無認(rèn)證狀態(tài)這是個(gè)高危風(fēng)險(xiǎn)操作。我用一個(gè)表格把整個(gè)流程整理出來步驟操作目的1關(guān)閉mysqld釋放文件鎖安全變更2配置skip-grant-tables臨時(shí)跳過權(quán)限認(rèn)證3無密碼登錄進(jìn)入管理員會話4FLUSH PRIVILEGES ALTER USER重新加載權(quán)限表并改密碼5還原配置并重啟恢復(fù)正常安全模式3.3 場景B正常修改業(yè)務(wù)賬號密碼如果沒忘記密碼只是例行輪換或者程序要換賬號那就完全用不著跳過權(quán)限表。直接登錄進(jìn)去執(zhí)行ALTER USER就行但里面有幾個(gè)細(xì)節(jié)值得說。比如我想把某個(gè)業(yè)務(wù)賬號的密碼改掉ALTER USER app_user% IDENTIFIED BY App2024Secret;如果要同時(shí)改密碼和認(rèn)證插件可以這么寫ALTER USER app_user% IDENTIFIED WITH mysql_native_password BY App2024Secret;還有一個(gè)容易被忽視的點(diǎn)在MySQL 5.7及以上版本密碼是有過期策略的。如果你在配置里開啟了default_password_lifetime或者單獨(dú)給某個(gè)賬號設(shè)置了PASSWORD EXPIRE那么密碼到期后客戶端就會報(bào)Your password has expired除非你讓賬號永不過期ALTER USER app_user% IDENTIFIED BY App2024Secret PASSWORD EXPIRE NEVER;業(yè)務(wù)程序里如果用的是連接池改完密碼之后不能立刻覺得萬事大吉。連接池里的舊連接可能在密碼修改前已經(jīng)建立這些連接并不會因?yàn)槊艽a變了就斷開還能繼續(xù)跑一段時(shí)間。但如果連接池里的連接被回收后新建連接新連接就會用新密碼去認(rèn)證一旦程序配置里的密碼沒同步改就會看到間斷性的Access denied錯(cuò)誤。所以正確的操作順序是先改程序里的數(shù)據(jù)庫配置再改數(shù)據(jù)庫密碼最后滾動重啟應(yīng)用服務(wù)。3.4 順帶把連接數(shù)告警一起解決掉密碼問題處理完接下來看連接數(shù)告警。先登錄數(shù)據(jù)庫查看當(dāng)前連接狀況SHOW STATUS LIKE Threads_connected; SHOW VARIABLES LIKE max_connections;發(fā)現(xiàn)當(dāng)前連接已經(jīng)480而max_connections只有500確實(shí)快滿了。這種場景先用動態(tài)參數(shù)把連接數(shù)臨時(shí)拉高但參數(shù)調(diào)完之后還得確認(rèn)是不是有連接沒有被合理釋放排查有沒有慢查詢堆積。先臨時(shí)調(diào)整SET GLOBAL max_connections 800;這個(gè)命令實(shí)時(shí)生效不用重啟但重啟后就沒了。要永久生效還得在/etc/my.cnf的[mysqld]段里加上max_connections800。注意不能只改配置文件不重啟也不能只動態(tài)改不寫文件兩者必須同時(shí)做否則下次重啟又會回到老配置。除了調(diào)連接數(shù)我還順手看了一下慢查詢?nèi)罩竞蚿rocesslistSHOW FULL PROCESSLIST;發(fā)現(xiàn)有幾條SQL跑了好幾十秒才導(dǎo)致連接堆積。這就靠slow_query_log和long_query_time設(shè)置得合理才能及時(shí)發(fā)現(xiàn)。很多生產(chǎn)事故前期都是連接數(shù)緩慢爬坡本質(zhì)是SQL性能太差光調(diào)大連接數(shù)等于治標(biāo)不治本。3.5 配置文件修改后的啟動校驗(yàn)改完配置文件最怕的就是參數(shù)寫錯(cuò)導(dǎo)致MySQL啟動失敗。如果你運(yùn)氣好語法錯(cuò)誤會在啟動時(shí)報(bào)出來并給出具體的錯(cuò)誤行。但有些參數(shù)的值不合規(guī)可能MySQL只是警告一下其中某些參數(shù)可能直接忽略某些參數(shù)導(dǎo)致啟動失敗。比如把max_connections設(shè)置成abc啟動會直接報(bào)錯(cuò)把port設(shè)置成一個(gè)已被占用的端口也會啟動失敗。在啟動之前強(qiáng)烈建議先用以下方式校驗(yàn)配置mysqld --validate-config --defaults-file/etc/my.cnf這個(gè)命令能幫你檢查配置文件里有沒有語法錯(cuò)誤、有沒有未知參數(shù)、參數(shù)取值是否合法。注意不同版本對--validate-config的支持略有差異在8.0里通常寫作mysqld --validate-config在5.7里可能需要先確認(rèn)。如果這個(gè)命令沒有輸出基本說明配置層面沒問題可以放心啟動。啟動后再次確認(rèn)參數(shù)是否真的加載了mysql -u root -p -e SHOW VARIABLES LIKE max_connections; SHOW VARIABLES LIKE port;如果發(fā)現(xiàn)啟動值和配置里寫的不一樣先懷疑是不是加載了別的配置文件用前面說的my_print_defaults去查。4. 我踩過的坑常見問題與排查技巧實(shí)錄4.1 改完密碼后程序連續(xù)報(bào)Access denied這是一個(gè)高頻問題。最常見的原因是改密碼時(shí)沒有把host匹配規(guī)則考慮進(jìn)去。比如程序連接串里寫的是jdbc:mysql://dbhost:3306/appdb但MySQL里創(chuàng)建的是app_userlocalhost這樣遠(yuǎn)程連接就會被拒絕。排查步驟如下先確認(rèn)程序是從哪臺機(jī)器、用什么賬號連的在數(shù)據(jù)庫里執(zhí)行SELECT user, host FROM mysql.user;看有沒有匹配記錄如果只有l(wèi)ocalhost記錄就新建一條app_user%或用具體IP創(chuàng)建再檢查賬號的認(rèn)證插件MySQL 8.0對齊一下客戶端驅(qū)動版本另外一個(gè)隱蔽原因是密碼中包含了特殊字符比如分號或反斜杠。在命令行里執(zhí)行ALTER USER ... IDENTIFIED BY a;b通常沒問題但在shell腳本里如果字符串拼接沒處理好后半段可能會被當(dāng)成新的SQL語句。所以建議密碼盡量不要包含;、、\這些需要多轉(zhuǎn)義一層的字符或者在腳本里先變量化再執(zhí)行。4.2 配置文件改了但不生效這是最折磨人的問題明明文件里改了端口重啟后還是3306。按照我的經(jīng)驗(yàn)優(yōu)先級依次排查這幾件事啟動命令是否帶--defaults-file參數(shù)指定了別的文件當(dāng)前系統(tǒng)加載的配置文件到底是不是你改的那個(gè)用my_print_defaults驗(yàn)證參數(shù)是否放到了正確的段里比如把max_connections放在了[client]段那服務(wù)端根本不讀是否因?yàn)閰?shù)屬于只讀啟動參數(shù)且由于某些版本差異動態(tài)修改被之后的重啟覆蓋是否改完配置文件之后服務(wù)沒有真正重啟只是停在了那里有個(gè)很典型的案例某次我在一臺機(jī)器上改了端口從3306改成3307重啟后依然是3306。排查發(fā)現(xiàn)是啟動腳本里先寫死了--port3306命令行參數(shù)優(yōu)先于配置文件配置怎么改都沒用。以后遇到配置不生效的現(xiàn)象先看ps aux | grep mysqld的完整參數(shù)列表能省下很多時(shí)間。4.3 使用skip-grant-tables模式后無法正常登錄有人跳過權(quán)限驗(yàn)證后執(zhí)行ALTER USER時(shí)報(bào)錯(cuò)Operation requires elevated privileges。這個(gè)原因我前面說過了skip-grant-tables模式下權(quán)限系統(tǒng)未加載需要先FLUSH PRIVILEGES讓權(quán)限表加載起來。另外在這種模式下MySQL可能拒絕某些賬號的遠(yuǎn)程連接因?yàn)樗虞d不到權(quán)限數(shù)據(jù)時(shí)行為會比較保守所以建議全程只在本機(jī)操作socket連接。還有個(gè)小細(xì)節(jié)在skip-grant-tables模式下如果你要重啟服務(wù)千萬確認(rèn)配置文件里那一行已經(jīng)被注釋否則服務(wù)啟動后又會跳過權(quán)限驗(yàn)證。有些老手會在操作后忘記這個(gè)步驟導(dǎo)致數(shù)據(jù)庫在公網(wǎng)上裸奔這是不能接受的安全風(fēng)險(xiǎn)。4.4 常見問題速查表問題現(xiàn)象最可能原因快速處理Access denied using password YES密碼錯(cuò)或host不匹配核對用戶host組合重設(shè)密碼Authentication plugin cannot be loaded客戶端驅(qū)動太老升級驅(qū)動或改用mysql_native_passwordYour password has expired密碼過期策略觸發(fā)ALTER USER ... PASSWORD EXPIRE NEVER改完配置重啟不生效加載了別的配置文件用ps和my_print_defaults確認(rèn)連接數(shù)到了max新連接等待慢查詢堆積或參數(shù)過小臨時(shí)SET GLOBAL調(diào)大優(yōu)化SQLUnknown variable xxx參數(shù)名寫錯(cuò)或版本不支持對照官方文檔檢查參數(shù)名Table mysql.user doesnt exist數(shù)據(jù)目錄損壞或初始化未完成備份數(shù)據(jù)后重新初始化系統(tǒng)庫5. 關(guān)于改密和配置的最佳實(shí)踐建議5.1 密碼管理和賬號權(quán)限的最小化無論你是給root改密碼還是給業(yè)務(wù)賬號設(shè)密碼都建議遵循最小化授權(quán)原則。日常開發(fā)調(diào)試可以不用root而應(yīng)該創(chuàng)建獨(dú)立的業(yè)務(wù)賬號只授予它需要的庫表權(quán)限。比如CREATE USER app_read% IDENTIFIED BY ReadOnly123; GRANT SELECT ON appdb.* TO app_read%; CREATE USER app_write% IDENTIFIED BY WriteOnly123; GRANT INSERT, UPDATE, DELETE ON appdb.* TO app_write%;這樣就算某個(gè)賬號密碼泄露影響范圍也被控制在較小邊界內(nèi)。同時(shí)密碼本身盡量做到復(fù)雜且唯一不要在多個(gè)環(huán)境復(fù)用同一個(gè)密碼。生產(chǎn)環(huán)境建議用密碼管理器生成隨機(jī)串并把密碼輪換做成定期任務(wù)而不是等到出了問題再改。5.2 配置文件管理的版本化和變更流程配置文件是數(shù)據(jù)庫的生命線改之前強(qiáng)烈建議先備份一份帶時(shí)間戳的副本cp /etc/my.cnf /etc/my.cnf.bak.$(date %F_%H-%M-%S)如果改完出了問題可以秒級回滾。更進(jìn)一步建議用代碼倉庫管理配置模板把開發(fā)、測試、生產(chǎn)三套配置文件都納入版本管理但每套環(huán)境的密碼和密鑰要單獨(dú)通過環(huán)境變量或secrets管理不要直接寫死在配置里。變更之前先規(guī)劃好變更窗口確認(rèn)變更影響面。動態(tài)參數(shù)可以先在線修改驗(yàn)證靜態(tài)參數(shù)則需要計(jì)劃維護(hù)窗口重啟。一個(gè)穩(wěn)妥的流程是先在測試環(huán)境完整執(zhí)行一遍變更腳本確認(rèn)無誤后再上生產(chǎn)執(zhí)行同樣步驟。5.3 提升穩(wěn)定性的幾個(gè)順手配置最后分享幾個(gè)我實(shí)際用下來能顯著減少問題的配置項(xiàng)[mysqld] skip_name_resolveON log_error_verbosity3 max_allowed_packet64M wait_timeout600 interactive_timeout600skip_name_resolve讓MySQL不對客戶端IP做反向DNS解析可以避免因?yàn)镈NS抖動導(dǎo)致連接緩慢但要注意它要求所有授權(quán)記錄里寫的都是IP而不是主機(jī)名。max_allowed_packet調(diào)大一些可以防止導(dǎo)入數(shù)據(jù)或大SQL報(bào)錯(cuò)。wait_timeout和interactive_timeout則控制非交互連接和交互連接的閑置超時(shí)時(shí)間避免大量空閑連接占著連接數(shù)不放。還有一個(gè)容易被忽略的小技巧如果你同時(shí)在維護(hù)多個(gè)MySQL實(shí)例建議每個(gè)實(shí)例都單獨(dú)建一份配置文件用--defaults-file指向各自文件這樣互不干擾。實(shí)例之間可以通過不同的端口和socket路徑區(qū)分配置隔離避免改了一個(gè)實(shí)例影響了另一個(gè)實(shí)例。我個(gè)人在實(shí)際操作中最深的體會是改密碼和調(diào)配置看起來是零碎的小活但它們最考驗(yàn)一個(gè)人對MySQL運(yùn)行機(jī)制的理解。你理解了權(quán)限表的匹配規(guī)則、理解了配置文件的加載優(yōu)先級、理解了靜態(tài)參數(shù)和動態(tài)參數(shù)的區(qū)別再遇到那些亂七八糟的連不上、不生效問題基本都能在幾分鐘內(nèi)定位到根因。希望這篇文章里記下的這些操作過程和踩坑記錄能幫你少走一些我當(dāng)年走過的彎路。