本次SQL優化是針對javaweb中的表格查詢作的。java
部分網絡架構圖mysql
N個機臺將業務數據發送至服務器,服務器程序將數據入庫至MySQL數據庫。服務器中的javaweb程序將數據展現到網頁上供用戶查看。程序員
windows單機主從分離web
已分表分庫,按年分庫,按天分表sql
每張表大概20w左右的數據數據庫
3天數據查詢70-80s編程
3-5swindows
沒法使用sql分頁,只能用java作分頁。數組
若是你配置了druid,可在druid頁面中直接查看sql執行時間和uri請求時間緩存
在後臺代碼中用System.currentTimeMillis計算時間差。
結論 : 後臺慢,且查詢sql慢
sql拼接過長,達到了3000行,有的甚至到8000行,大多都是union all的操做,且有沒必要要的嵌套查詢和查詢了沒必要要的字段
利用explain查看執行計劃,where條件中除時間外只有一個字段用到了索引
備註 : 因優化完了,以前的sql實在找不到了,這裏只能YY了。
效果沒那麼明顯
效果沒那麼明顯
將union all的操做分解,例如(一個union all的sql也很長)
select aa from bb_2018_10_01 left join ... on .. left join .. on .. where ..
union all
select aa from bb_2018_10_02 left join ... on .. left join .. on .. where ..
union all
select aa from bb_2018_10_03 left join ... on .. left join .. on .. where ..
union all
select aa from bb_2018_10_04 left join ... on .. left join .. on .. where ..
將如上sql分解成若干個sql去執行,最終彙總數據,最後快了20s左右。
select aa from bb_2018_10_01 left join ... on .. left join .. on .. where ..
利用java異步編程的操做,將分解的sql異步執行並最終彙總數據。這裏用到了CountDownLatch和ExecutorService,示例代碼以下:
// 獲取時間段全部天數
List<String> days = MyDateUtils.getDays(requestParams.getStartTime(), requestParams.getEndTime());
// 天數長度
int length = days.size();
// 初始化合並集合,並指定大小,防止數組越界
List<你想要的數據類型> list = Lists.newArrayListWithCapacity(length);
// 初始化線程池
ExecutorService pool = Executors.newFixedThreadPool(length);
// 初始化計數器
CountDownLatch latch = new CountDownLatch(length);
// 查詢天天的時間併合並
for (String day : days) {
Map<String, Object> param = Maps.newHashMap();
// param 組裝查詢條件
pool.submit(new Runnable() {
@Override
public void run() {
try {
// mybatis查詢sql
// 將結果彙總
list.addAll(查詢結果);
} catch (Exception e) {
logger.error("getTime異常", e);
} finally {
latch.countDown();
}
}
});
}
try {
// 等待全部查詢結束
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
// list爲彙總集合
// 若是有必要,能夠組裝下你想要的業務數據,計算什麼的,若是沒有就沒了
結果又快了20-30s
如下是個人配置示例。加了skip-name-resolve,快了4-5s。其餘配置自行判定
[client]
port=3306
[mysql]
no-beep
default-character-set=utf8
[mysqld]
server-id=2
relay-log-index=slave-relay-bin.index
relay-log=slave-relay-bin
slave-skip-errors=all #跳過全部錯誤
skip-name-resolve
port=3306
datadir="D:/mysql-slave/data"
character-set-server=utf8
default-storage-engine=INNODB
sql-mode="STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"
log-output=FILE
general-log=0
general_log_file="WINDOWS-8E8V2OD.log"
slow-query-log=1
slow_query_log_file="WINDOWS-8E8V2OD-slow.log"
long_query_time=10
# Binary Logging.
# log-bin
# Error Logging.
log-error="WINDOWS-8E8V2OD.err"
# 整個數據庫最大鏈接(用戶)數
max_connections=1000
# 每一個客戶端鏈接最大的錯誤容許數量
max_connect_errors=100
# 表描述符緩存大小,可減小文件打開/關閉次數
table_open_cache=2000
# 服務所能處理的請求包的最大大小以及服務所能處理的最大的請求大小(當與大的BLOB字段一塊兒工做時至關必要)
# 每一個鏈接獨立的大小.大小動態增長
max_allowed_packet=64M
# 在排序發生時由每一個線程分配
sort_buffer_size=8M
# 當全聯合發生時,在每一個線程中分配
join_buffer_size=8M
# cache中保留多少線程用於重用
thread_cache_size=128
# 此容許應用程序給予線程系統一個提示在同一時間給予渴望被運行的線程的數量.
thread_concurrency=64
# 查詢緩存
query_cache_size=128M
# 只有小於此設定值的結果纔會被緩衝
# 此設置用來保護查詢緩衝,防止一個極大的結果集將其餘全部的查詢結果都覆蓋
query_cache_limit=2M
# InnoDB使用一個緩衝池來保存索引和原始數據
# 這裏你設置越大,你在存取表裏面數據時所須要的磁盤I/O越少.
# 在一個獨立使用的數據庫服務器上,你能夠設置這個變量到服務器物理內存大小的80%
# 不要設置過大,不然,因爲物理內存的競爭可能致使操做系統的換頁顛簸.
innodb_buffer_pool_size=1G
# 用來同步IO操做的IO線程的數量
# 此值在Unix下被硬編碼爲4,可是在Windows磁盤I/O可能在一個大數值下表現的更好.
innodb_read_io_threads=16
innodb_write_io_threads=16
# 在InnoDb核心內的容許線程數量.
# 最優值依賴於應用程序,硬件以及操做系統的調度方式.
# 太高的值可能致使線程的互斥顛簸.
innodb_thread_concurrency=9
# 0表明日誌只大約每秒寫入日誌文件而且日誌文件刷新到磁盤.
# 1 ,InnoDB會在每次提交後刷新(fsync)事務日誌到磁盤上
# 2表明日誌寫入日誌文件在每次提交後,可是日誌文件只有大約每秒纔會刷新到磁盤上
innodb_flush_log_at_trx_commit=2
# 用來緩衝日誌數據的緩衝區的大小.
innodb_log_buffer_size=16M
# 在日誌組中每一個日誌文件的大小.
innodb_log_file_size=48M
# 在日誌組中的文件總數.
innodb_log_files_in_group=3
# 在被回滾前,一個InnoDB的事務應該等待一個鎖被批准多久.
# InnoDB在其擁有的鎖表中自動檢測事務死鎖而且回滾事務.
# 若是你使用 LOCK TABLES 指令, 或者在一樣事務中使用除了InnoDB之外的其餘事務安全的存儲引擎
# 那麼一個死鎖可能發生而InnoDB沒法注意到.
# 這種狀況下這個timeout值對於解決這種問題就很是有幫助.
innodb_lock_wait_timeout=30
# 開啓定時
event_scheduler=ON
快4-5s
效果沒那麼明顯
針對這條,我自身以爲很詫異。原sql,b爲索引
select aa from bb_2018_10_02 left join ... on .. left join .. on .. where b = 'xxx'
應該以前有union all,union all是一個一個的執行,最後彙總的結果。修改成
select aa from bb_2018_10_02 left join ... on .. left join .. on .. inner join
(
select 'xxx1' as b2
union all
select 'xxx2' as b2
union all
select 'xxx3' as b2
union all
select 'xxx3' as b2
) t on b = t.b2
結果快了3-4s
根據以上操做,3天查詢效率已經達到了8s左右,再也快不了了。查看mysql的cpu使用率和內存使用率都不高,到底爲何查這麼慢了,3天最多才60w數據,關聯的也都是一些字典表,不至於如此。繼續根據網上提供的資料,一系列騷操做,基本沒用,沒轍。
因分析過sql優化已經ok了,試想是否是磁盤讀寫問題。將優化過的程序,分別部署於不一樣的現場環境。一個有ssd,一個沒有ssd。發現查詢效率懸殊。用軟件檢測過發現ssd讀寫速度在700-800M/s,普通機械硬盤讀寫在70-80M/s。
優化結果:達到預期。
優化結論:sql優化不只僅是對sql自己的優化,還取決於自己硬件條件,其餘應用的影響,外加自身代碼的優化。
優化的過程是自身的一個歷練和考驗,珍惜這種機會,不作只寫業務代碼的程序員。但願以上能夠有助於你的思考,不足之處望指正。
歡迎關注微信公衆號:IT哈哈
做者:小祝特煩惱
來源:https://my.oschina.net
/xiaozhutefannao/blog/2243432