根據實際狀況:如限制varchar字段長度,設置定長的char;使用enum。html
根據實際狀況:可以改寫爲join的儘可能不要使用子查詢。
//子查詢/left join/left semi joinmysql
記錄慢查詢日誌,逐條分析。面試
建立測試表:sql
CREATE TABLE `goods` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(128) COLLATE utf8mb4_general_ci NOT NULL, `price` FLOAT(10,3) UNSIGNED NOT NULL DEFAULT '0.000' COMMENT '價格'; `info` varchar(255) COLLATE utf8mb4_general_ci NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='商品表'; CREATE TABLE `goods_order_log` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `order_id` int(10) unsigned NOT NULL COMMENT '訂單id', `buy_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '購買時間', `goods_id` int(10) unsigned NOT NULL COMMENT '訂單id中:商品id', `goods_num` int(10) unsigned NOT NULL COMMENT '訂單id中:商品id的數量', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='商品購買流水錶'; CREATE TABLE `goods_fav_log` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `user_id` int(10) unsigned NOT NULL COMMENT '蒐藏的用戶id', `goods_id` int(10) unsigned NOT NULL COMMENT '商品id', `buy_time` `fav_value` ENUM('1','0') NOT NULL DEFAULT '1' COMMENT '是否蒐藏', PRIMARY KEY (`id`), KEY `user_id` (`user_id`), KEY `goods_id` (`goods_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=COMPACT COMMENT='商品蒐藏流水錶'
填充測試數據的存儲過程:數據庫
DELIMITER $$ CREATE PROCEDURE `insert_goods`(IN `c` INT UNSIGNED) NO SQL COMMENT '生成測試數據的存儲過程' BEGIN declare i int default 0; set autocommit = 0; repeat set i = i + 1; insert into goods(name,price,info) values(concat(i,'_',rand_string(60)),floor(RAND()*100000)/100,rand_string(120)); until i = c end repeat; set autocommit = 1; END$$ DELIMITER $$ CREATE PROCEDURE `insert_goods_order_log`(IN `c` INT UNSIGNED) NO SQL COMMENT '生成訂單流水的存儲過程' BEGIN declare i int default 0; set autocommit = 0; set @basetime = '2016-01-01 00:00:00'; repeat set i = i + 1; set @orderid = concat(REPLACE(REPLACE(REPLACE(NOW(),'-',''),' ',''),':',''), '_',rand_string(6)); set @goodsnums = floor(RAND()*10) + 1; set @buytime = from_unixtime(UNIX_TIMESTAMP(@basetime) + floor((UNIX_TIMESTAMP()-UNIX_TIMESTAMP(@basetime))*RAND())); while @goodsnums>0 do set @goodid = floor(RAND() * 10000) + 1; insert into goods_order_log(order_id,buy_time,goods_id,goods_num) values(@orderid,@buytime,@goodid,RAND()*50); set @goodsnums = @goodsnums - 1; end while; until i = c end repeat; set autocommit = 1; END$$ DELIMITER ; DELIMITER $$ CREATE PROCEDURE `insert_goods_fav_log`(IN `c` INT UNSIGNED) NO SQL COMMENT '生成用戶蒐藏商品的存儲過程' BEGIN declare i int default 0; set autocommit = 0; repeat set i = i + 1; set @userid = floor(RAND()*100000) + 1; set @goodsid = floor(RAND()*100000) + 1; select @userid,@goodsid,FLOOR(RAND()*2); insert into goods_fav_log(user_id,goods_id,fav_value) values(@userid,@goodsid,concat(FLOOR(RAND()*2),'')); until i = c end repeat; set autocommit = 1; END$$ DELIMITER ; call insert_goods(10000); //商品表,1w call insert_goods_order_log(1000000); //訂單詳情表,100w call insert_goods_fav_log(100000); //商品收藏表,10w
//(部分商品)區間銷量排行 select goods_id,sum(goods_num) as goods_nums from goods_order_log where goods_id between 1 and 199 group by goods_id order by goods_nums desc limit 0,20; ##銷售額排行 //子查詢 select a.id,b.goods_nums,a.price,(b.goods_nums*a.price) as goods_sales from goods as a inner join (select goods_id,sum(goods_num) as goods_nums from goods_order_log where goods_id between 1 and 1999 group by goods_id ) as b on a.id=b.goods_id order by goods_sales desc limit 0,20; //商品收藏排行 select goods_id,count(id) as fav_nums from goods_fav_log where fav_value='1' group by goods_id order by fav_nums desc limit 0,20;
真正好的優化,是須要創建一套施行的標準,在起步時即達到最優。服務器
《MySQL 數據庫鐵律 2019.09》
《詳解Mysql 30條軍規》併發
基於硬件的系統優化:《面試之Mysql優化問題》
優化效果:硬件 << 系統配置 << 數據庫表結構 << SQL及索引
課程樹優化:《MySQL優化/面試,看這一篇就夠了》
開啓sql_cache配置;大表分區;配置並查看slow.log日誌(線上可開啓),配置並查看profile信息(線上建議關閉);壓測工具mysqlslap。工具
《MySQL自帶的性能壓力測試工具mysqlslap詳解》性能
/usr/local/mysql/bin/mysqlslap --auto-generate-sql -uroot -p 結果中各項含義: Average number of … 運行全部語句的平均秒數 Minimum number of … 運行全部語句的最小秒數 Maximum number of … 運行全部語句的最大秒數 Number of clients … 客戶端數量 Average number of queries per client 每一個客戶端運行查詢的 平均數
/usr/local/mysql/bin/mysqlslap --auto-generate-sql --concurrency=100 –number-of-queries=1000 -uroot -p –concurrency=100 指定同時有100個客戶端鏈接 –number-of-queries=1000 指定總的測試查詢次數(併發客戶端數 \* 每一個客戶端的查詢次數)
**經常使用參數 \[options\] 詳細說明:** --auto-generate-sql, -a 自動生成測試表和數據,表示用mysqlslap工具本身生成的SQL腳原本測試併發壓力。 --auto-generate-sql-load-type=type 測試語句的類型。表明要測試的環境是讀操做仍是寫操做仍是二者混合的。取值包括:read,key,write,update和mixed(默認)。 --auto-generate-sql-add-auto-increment 表明對生成的表自動添加auto_increment列,從5.1.18版本開始支持。 --number-char-cols=N, -x N 自動生成的測試表中包含多少個字符類型的列,默認1 --number-int-cols=N, -y N 自動生成的測試表中包含多少個數字類型的列,默認1 --number-of-queries=N 總的測試查詢次數(併發客戶數×每客戶查詢次數) --query=name,-q 使用自定義腳本執行測試,例如能夠調用自定義的一個存儲過程或者sql語句來執行測試。 --create-schema 表明自定義的測試庫名稱,測試的schema,MySQL中schema也就是[database](http://www.ha97.com/category/database)。 --commint=N 多少條DML後提交一次。 --compress, -C 若是服務器和客戶端支持都壓縮,則壓縮信息傳遞。 --concurrency=N, -c N 表示併發量,也就是模擬多少個客戶端同時執行select。可指定多個值,以逗號或者--delimiter參數指定的值作爲分隔符。例如:--concurrency=100,200,500。 --engine=engine_name, -e engine_name 表明要測試的引擎,能夠有多個,用分隔符隔開。例如:--engines=myisam,innodb。 --iterations=N, -i N 測試執行的迭代次數,表明要在不一樣併發環境下,各自運行測試多少次。 --only-print 只打印測試語句而不實際執行。 --detach=N 執行N條語句後斷開重連。 --debug-info, -T 打印內存和CPU的相關信息。 說明: 測試的過程須要生成測試表,插入測試數據,這個mysqlslap能夠自動生成,默認生成一個mysqlslap的schema,若是已經存在則先刪除。能夠用`--only-print`來打印實際的測試過程,整個測試完成後不會在數據庫中留下痕跡。