1. 2019-03-05 Sparksql集群性能調優 CheatSheet
0.買高性能機器,增加節點
1.設置磁碟文件預讀值大小為16384,使用linux命令:
echo 16384 > /sys/block/{磁碟名}/queue/read_ahead_kb
2. Spark 任務序列化只支持JavaSerializer,數據序列化支持JavaSerializer和 KryoSerializer 。KryoSerializer能達到JavaSerializer的十倍。
3.在spark.driver.extraJavaOptions和spark.executor.extraJavaOptions配置項中添加參數:" -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps ",如果頻繁出現Full GC,需要優化GC。把RDD做Cache操作,通過日誌查看RDD在內存中的大小,如果數據太大,需要改變RDD的存儲級別來優化。
4.一般並行度設置為集群CPU總和的2-3倍
5.大表和小表做join操作時可以把小表Broadcast到各個節點,從而就可以把join操作轉變成普通的操作,減少了shuffle操作。
6. 合理設計DAG,減少shuffle //TODO
7.使用 mapPartitions 可以更靈活地操作數據,例如對一個很大的數據求TopN,當N不是很大時,可以先使用mapPartitions對每個partition求TopN,collect結果到本地之後再做排序取TopN。這樣相比直接對全量數據做排序取TopN效率要高很多。
8.當之前的操作有很多filter時,使用 coalesce 減少空運行的任務數量
9.當任務數過大時候Shuffle壓力太大導致程序掛住不動,或者出現linux資源受限的問題。此時需要對數據重新進行分區,使用 repartition 。
10.配置多個磁碟給 localDir ,shuffle時寫入數據速度增快
11. 別collect大數據量,數據會回到driver端,容易OOM。非要collect,請配置 spark.sql.bigdata.thriftServer.useHdfsCollect 為true,會存在hdfs再讀
12.盡量用receByKey,會在Map端做本地聚合
13. broadcase set/map而不是Iterator, set/map 查詢效率O(1) ,iteratorO(n)
14. 數據發生傾斜,repartition大法 ,查出key,salt it
15.使用Hash Shuffle時,通過設置 spark.shuffle.consolidateFiles 為true,來合並shuffle中間文件,減少shuffle文件的數量,減少文件IO操作以提升性能
16.Spark SQL 小表join,把小表broadcast出去。配置 spark.sql.autoBroadcastJoinThreshold 和 spark.sql.bigdata.useExecutorBroadcast 。小表在join 右端。
17.SparkSQL數據傾斜,配置 spark.sql.planner.skewJoin 和 spark.sql.planner.skewJoin.threshold
18. SparkSQL 小文件,配置 spark.sql.small.file.combine 和 spark.sql.small.file.split.size
2. sql server資料庫查詢慢怎麼優化
在安裝有SQLServer資料庫的計算機上,我們在使用資料庫的過程中,有時候會在任務管理器里發現sqlservr.exe這個進程的內存和CPU佔用率較高。
接下來我們來看一下,如何解決上面這個問題,需要設置SQLServer資料庫的內存配置。登錄資料庫,這里使用的是SQLServer2008,右鍵點擊最上方的伺服器名,在彈出的菜單中,點擊【屬性】
打開伺服器屬性窗口。默認顯示的是第一項【常規】內容,點擊第二項【內存】進行內存配置。
點擊【內存】後,打開伺服器內存選項配置界面。這里的【使用AWE分配內存】可以對內存進行擴展支持,我們要做的是更改下方的最大伺服器內存。這個數值根據自己伺服器內存大小來做適當設置。
5
個人建議設置本機內存的一半或稍微高一點,如機器內存為2G,那麼我們這里填寫1000。需要注意的是內存設置調小以後,在資料庫執行較復雜SQL語句的時候,可能會比較慢,出現這種情況,我們再適當上調最大內存配置大小。
3. 如何解決SQL查詢速度太慢
1. 執行計劃中明明有使用到索引,為什麼執行還是這么慢?
2. 執行計劃中顯示掃描行數為 644,為什麼 slow log 中顯示 100 多萬行?
a. 我們先看執行計劃,選擇的索引 「INDX_BIOM_ELOCK_TASK3(TASK_ID)」。結合 sql 來看,因為有 "ORDER BY TASK_ID DESC" 子句,排序通常很慢,如果使用了文件排序性能會更差,優化器選擇這個索引避免了排序。
那為什麼不選 possible_keys:INDX_BIOM_ELOCK_TASK 呢?原因也很簡單,TASK_DATE 欄位區分度太低了,走這個索引需要掃描的行數很大,而且還要進行額外的排序,優化器綜合判斷代價更大,所以就不選這個索引了。不過如果我們強制選擇這個索引(用 force index 語法),會看到 SQL 執行速度更快少於 10s,那是因為優化器基於代價的原則並不等價於執行速度的快慢;
b. 再看執行計劃中的 type:index,"index" 代表 「全索引掃描」,其實和全表掃描差不多,只是掃描的時候是按照索引次序進行而不是行,主要優點就是避免了排序,但是開銷仍然非常大。
Extra:Using where 也意味著掃描完索引後還需要回表進行篩選。一般來說,得保證 type 至少達到 range 級別,最好能達到 ref。
在第 2 點中提到的「慢日誌記錄Rows_examined: 1161559,看起來是全表掃描」,這里更正為「全索引掃描」,掃描行數確實等於表的行數;
c. 關於執行計劃中:「rows:644」,其實這個只是估算值,並不準確,我們分析慢 SQL 時判斷准確的掃描行數應該以 slow log 中的 Rows_examined 為准。
4. 優化建議:添加組合索引 IDX_REL_DEVID_TASK_ID(REL_DEVID,TASK_ID)
優化過程:
TASK_DATE 欄位存在索引,但是選擇度很低,優化器不會走這個索引,建議後續可以刪除這個索引:
select count(*),count(distinct TASK_DATE) from T_BIOMA_ELOCK_TASK;+------------+---------------------------+| count(*) | count(distinct TASK_DATE) |+------------+---------------------------+| 1161559 | 223 |+------------+---------------------------+
在這個 sql 中 REL_DEVID 欄位從命名上看選擇度較高,通過下面 sql 來檢驗確實如此:
select count(*),count(distinct REL_DEVID) from T_BIOMA_ELOCK_TASK;+----------+---------------------------+| count(*) | count(distinct REL_DEVID) |+----------+---------------------------+| 1161559 | 62235 |+----------+---------------------------+
由於有排序,所以得把 task_id 也加入到新建的索引中,REL_DEVID,task_id 組合選擇度 100%:
select count(*),count(distinct REL_DEVID,task_id) from T_BIOMA_ELOCK_TASK;+----------+-----------------------------------+| count(*) | count(distinct REL_DEVID,task_id) |+----------+-----------------------------------+| 1161559 | 1161559 |+----------+-----------------------------------+
在測試環境添加 REL_DEVID,TASK_ID 組合索引,測試 sql 性能:alter table T_BIOMA_ELOCK_TASK add index idx_REL_DEVID_TASK_ID(REL_DEVID,TASK_ID);
添加索引後執行計劃:
這里還要注意一點「隱式轉換」:REL_DEVID 欄位數據類型為 varchar,需要在 sql 中加引號:AND T.REL_DEVID = 000000025xxx >> AND T.REL_DEVID = '000000025xxx'
執行時間從 10s+ 降到 毫秒級別:
1 row in set (0.00 sec)
結論
一個典型的 order by 查詢的優化,添加更合適的索引可以避免性能問題:執行計劃使用索引並不意味著就能執行快。
4. 如何查找MySQL中查詢慢的SQL語句
一、MySQL資料庫有幾個配置選項可以幫助我們及時捕獲低效SQL語句
1,slow_query_log
這個參數設置為ON,可以捕獲執行時間超過一定數值的SQL語句。
2,long_query_time
當SQL語句執行時間超過此數值時,就會被記錄到日誌中,建議設置為1或者更短。
3,slow_query_log_file
記錄日誌的文件名。
4,log_queries_not_using_indexes
這個參數設置為ON,可以捕獲到所有未使用索引的SQL語句,盡管這個SQL語句有可能執行得挺快。
二、檢測mysql中sql語句的效率的方法
1、通過查詢日誌
(1)、Windows下開啟MySQL慢查詢
MySQL在Windows系統中的配置文件一般是是my.ini找到[mysqld]下面加上
代碼如下
log-slow-queries = F:/MySQL/log/mysqlslowquery。log
long_query_time = 2
(2)、Linux下啟用MySQL慢查詢
MySQL在Windows系統中的配置文件一般是是my.cnf找到[mysqld]下面加上
代碼如下
log-slow-queries=/data/mysqldata/slowquery。log
long_query_time=2
5. 最近我的資料庫(sql)查詢速度很慢,這是什麼原因
查詢慢是和表結構,語句,系統等相關的 建索引等方法都可以改善表結構, 另外如果返回數據量很大,當然會慢,所以你盡量查詢相對有用的數據 再就是查詢語句了 比如用in查詢沒有jion查詢快,還有 between 改成 > <會快 再還有,用子查詢也會慢很多, 如果是一些很復雜的查詢,可以改用存儲過程會好點,有時用臨時表會慢但,從海量數據中查詢取數進行子查詢又不如用臨時錶快,不同的問題用不同的解決方法,看你要哪種了,單看你的問題無法直接判斷。 不過,優化查詢句是關鍵的了。
6. sql 查詢結果太多(數萬條),導致運行很慢,甚至內存不足出現問題。有什麼好的方法可以解決這個問題
我試過一下幾個方法:
嘗試把多餘的進程關閉了,增加內存,這樣速度和查詢條數都會增多
如果有多個關聯條件,並且可以拆分,建議用UNION ALL進行查詢,效率會有所提高
如果你只要查詢幾千條看看效果,那樓上的朋友的建議也是可取的
嘗試下查詢中,使用索引列,速度也會有明顯增加
具體情況具體分析,籠統的我也就知道這些了,我也是新手哇
7. 記錄一次慢sql排查
mysql的慢日誌中,看到有這么一條
不算太復雜的一條sql,但是掃了200多萬行的數據,所以慢。先看執行計劃
mysql> explain SELECT
-> lu.userId,
-> lu.userName,
-> lu.photo userImage,
-> lu.sex,
-> wc.typeId AS courseType,
-> wc.name AS courseName,
-> lc.className,
-> DATE_FORMAT(wcu.CreatedTime, '%Y-%m-%d %H:%i:%s') AS time
-> FROM
-> wkt_courseclassuser wcu
-> INNER JOIN wkt_course wc on wcu.courseId = wc.id
-> INNER JOIN lxx_user lu ON wcu.userId = lu.userId
-> INNER JOIN (select t.* from (select * from lxx_classuserrecord WHERE classtypeid =1 ORDER BY CreateTime desc) t GROUP BY t.userid ) lcur ON lcur.userid = lu.userId
-> INNER JOIN lxx_class lc on lc.classId = lcur.classId
-> INNER JOIN lxx_registerschool lr ON lr.schoolKey = lu.schoolKey
-> WHERE lc.status = 1 and lc.typeId = 1
-> and lr.schoolId = 60800000000000001
-> and wc.typeId in (1,2,3,23)
-> ORDER BY wc.CreateTime DESC
-> LIMIT 100;
+----+-------------+---------------------+--------+----------------------------------------------------------------------------------------------------------------+----------------------------------+---------+-------------------------+-------+----------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------------------+--------+----------------------------------------------------------------------------------------------------------------+----------------------------------+---------+-------------------------+-------+----------------------------------------------+
| 1 | PRIMARY | lr | ref | lxx_registerSchool_schoolId | lxx_registerSchool_schoolId | 9 | const | 1 | Using where; Using temporary; Using filesort |
| 1 | PRIMARY | lu | ref | PRIMARY,index_lxx_user_schoolKey | index_lxx_user_schoolKey | 603 | wkt_school.lr.schoolKey | 79 | Using where |
| 1 | PRIMARY | <derived2> | ref | <auto_key1> | <auto_key1> | 8 | wkt_school.lu.userId | 15 | Using where |
| 1 | PRIMARY | lc | eq_ref | PRIMARY | PRIMARY | 8 | lcur.classid | 1 | Using where |
| 1 | PRIMARY | wcu | ref | index_wkt_courseclassuser_courseId,index_wkt_courseclassuser_userId,index_wkt_courseclassuser_courseId_classId | index_wkt_courseclassuser_userId | 9 | wkt_school.lu.userId | 47 | Using where |
| 1 | PRIMARY | wc | eq_ref | PRIMARY | PRIMARY | 8 | wkt_school.wcu.courseId | 1 | Using where |
| 2 | DERIVED | <derived3> | ALL | NULL | NULL | NULL | NULL | 47204 | Using temporary; Using filesort |
| 3 | DERIVED | lxx_classuserrecord | ALL | NULL | NULL | NULL | NULL | 47204 | Using where; Using filesort |
+----+-------------+---------------------+--------+----------------------------------------------------------------------------------------------------------------+----------------------------------+---------+-------------------------+-------+----------------------------------------------+
乍一看好像最大的rows才47204,為什麼實際執行掃描行數要大這么多呢?
網上找到一篇解釋
https://dba.stackexchange.com/questions/73520/mysql-explain-has-different-row-count-than-slow-query-log
大概意思是,explain只是根據數據的特徵,大概估算要掃描的行數,實際執行時,特別是需要做join操作時,結果集都是n*m的,因此實際執行結果可能要大很多。
看到執行計劃最後兩行,都是需要Using filesort的。很明顯是產生於
INNER JOIN (select t.* from (select * from lxx_classuserrecord WHERE classtypeid =1 ORDER BY CreateTime desc) t GROUP BY t.userid ) lcur ON lcur.userid = lu.userId
一行。因為INNER JOIN的是一個子查詢的結果, 上面不會有索引 ,而且這個子查詢的結果集也有幾萬條,開始的直觀感覺是慢在這里。結果優化了很久,也沒什麼效果。最後把這個關聯條件也去掉了,發現查詢時間還是跟原來差不多,因此問題不是在此。
PS:第一次沒有看懂explain的結果。explain中的第三行從derived2的結果中,也就是id為2的那條派生表查詢中,自動建立了一個auto_key1的索引,因此inner join上面那行子查詢並不會很慢
東找西找,發現去掉ORDER BY wc.CreateTime DESC以後,就變得很快了。查看了一下wkt_course的索引,果然CreateTime沒有索引。趕緊補一下
CREATE INDEX index_wkt_course_CreateTime ON wkt_course(CreateTime)
然後再explain一下,
| 1 | PRIMARY | lr | ref | lxx_registerSchool_schoolId | lxx_registerSchool_schoolId | 9 | const | 1 | Using where; Using temporary; Using filesort |
第一行這里沒有任何改觀。實際執行起來也絲毫沒有變快。
再靜下來,仔細分析一下問題在哪裡。mysql估計是先執行了連表查詢,然後對這個結果集創建臨時表,然後進行排序,最後在取出前100。用select count(*) 在去掉limit限制後數了一下,這個結果集有80多萬條數據,怪不得排序很慢。這里總結出來一個經驗,就是看explain首先要關注Using temporary,其次是Using filesort的問題。
要使ORDER BY的欄位走索引,則需要讓欄位所在的表成為驅動表
https://blog.csdn.net/zerou8400/article/details/95389044
最終的解決方案,在order by的欄位建立索引,並且使用straight_join,強制指定wkt_course為驅動表
SELECT
lu.userId,
lu.userName,
lu.photo userImage,
lu.sex,
wc.typeId AS courseType,
wc.name AS courseName,
lc.className,
DATE_FORMAT(wcu.CreatedTime, '%Y-%m-%d %H:%i:%s') AS time
FROM
wkt_course wc
straight_join wkt_courseclassuser wcu ON wcu.courseId = wc.id
INNER JOIN lxx_user lu ON wcu.userId = lu.userId
INNER JOIN lxx_registerschool lr on lr.schoolKey = lu.schoolKey
INNER JOIN (select t.* from (select * from lxx_classuserrecord WHERE classtypeid =1 ORDER BY CreateTime desc) t GROUP BY t.userid ) lcur ON lcur.userid = lu.userId
INNER JOIN lxx_class lc on lc.classId = lcur.classId
WHERE lr.schoolId = 60800000000000001
and wc.typeId in (1,2,3,23)
and lc.status = 1 and lc.typeId = 1
ORDER BY wc.CreateTime DESC
LIMIT 100;
圍觀一下優化後的執行計劃
mysql> explain SELECT
-> lu.userId,
-> lu.userName,
-> lu.photo userImage,
-> lu.sex,
-> wc.typeId AS courseType,
-> wc.name AS courseName,
-> lc.className,
-> DATE_FORMAT(wcu.CreatedTime, '%Y-%m-%d %H:%i:%s') AS time
-> FROM
-> wkt_course wc
-> straight_join wkt_courseclassuser wcu ON wcu.courseId = wc.id
-> INNER JOIN lxx_user lu ON wcu.userId = lu.userId
-> INNER JOIN lxx_registerschool lr on lr.schoolKey = lu.schoolKey
-> INNER JOIN (select t.* from (select * from lxx_classuserrecord WHERE classtypeid =1 ORDER BY CreateTime desc) t GROUP BY t.userid ) lcur ON lcur.userid = lu.userId
-> INNER JOIN lxx_class lc on lc.classId = lcur.classId
-> WHERE lr.schoolId = 60800000000000001
-> and wc.typeId in (1,2,3,23)
-> and lc.status = 1 and lc.typeId = 1
-> ORDER BY wc.CreateTime DESC
-> LIMIT 100;
+----+-------------+---------------------+--------+----------------------------------------------------------------------------------------------------------------+------------------------------------+---------+-----------------------+-------+---------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------------------+--------+----------------------------------------------------------------------------------------------------------------+------------------------------------+---------+-----------------------+-------+---------------------------------+
| 1 | PRIMARY | wc | index | PRIMARY | index_wkt_course_CreateTime | 6 | NULL | 1 | Using where |
| 1 | PRIMARY | lr | ref | lxx_registerSchool_schoolId | lxx_registerSchool_schoolId | 9 | const | 1 | NULL |
| 1 | PRIMARY | wcu | ref | index_wkt_courseclassuser_courseId,index_wkt_courseclassuser_userId,index_wkt_courseclassuser_courseId_classId | index_wkt_courseclassuser_courseId | 9 | wkt_school.wc.id | 31 | Using where |
| 1 | PRIMARY | lu | eq_ref | PRIMARY,index_lxx_user_schoolKey | PRIMARY | 8 | wkt_school.wcu.userId | 1 | Using where |
| 1 | PRIMARY | <derived2> | ref | <auto_key1> | <auto_key1> | 8 | wkt_school.wcu.userId | 15 | Using where |
| 1 | PRIMARY | lc | eq_ref | PRIMARY | PRIMARY | 8 | lcur.classid | 1 | Using where |
| 2 | DERIVED | <derived3> | ALL | NULL | NULL | NULL | NULL | 36487 | Using temporary; Using filesort |
| 3 | DERIVED | lxx_classuserrecord | ALL | NULL | NULL | NULL | NULL | 36487 | Using where; Using filesort |
+----+-------------+---------------------+--------+----------------------------------------------------------------------------------------------------------------+------------------------------------+---------+-----------------------+-------+---------------------------------+
https://blog.csdn.net/m0_37894254/article/details/80675733
8. Greenplum集群部署和架構優化,我總結了5000字的心得
最近對離線數倉體系進行了擴容和架構改造,也算是一波三折,出了很多小插曲,有一些改進點對我們來說也是真空地帶,通過對比和模擬壓測總算是得到了預期的結果,這方面尤其值得一提的是郭運凱同學的敬業,很多前置的工作,優化和應用壓測的工作都是他完成的。
整體來說,整個事情的背景是因為伺服器硬體過保,剛好借著過保伺服器替換的機會來做集群架構的優化和改造。
1.集群架構改造的目標
在之前也總結過目前存在的一些潛在問題,也是本次部署架構改進的目標:
1)之前 的GP segment數量設計過度 ,因為資源限制,過多考慮了功能和性能,對於集群的穩定性和資源平衡性考慮有所欠缺,在每個物理機節點上部署了10個Primary,10個Mirror,一旦1個伺服器節點不可用,整個集群幾乎無法支撐業務。
2)GP集群 的存儲資源和性能的平衡不夠 ,GP存儲基於RAID-5,如果出現壞盤,磁碟重構的代價比較高,而且重構期間如果再出現壞盤,就會非常被動,而且對於離線數倉的數據質量要求較高,存儲容量相對不是很大,所以在存儲容量和性能的綜合之上,我們選擇了RAID-10。
3)集 群的異常場景的恢復需要完善, 集群在異常情況下(如伺服器異常宕機,數據節點不可用,伺服器後續過保實現節點滾動替換)的故障恢復場景測試不夠充分,導致在一些遷移和改造中,相對底氣不足,存在一些知識盲區。
4)集群版本過 低 ,功能和性能上存在改進空間。畢竟這個集群是4年前的版本,底層的PG節點的版本也比較舊了,在功能上和性能上都有一定的期望,至少能夠與時俱進。
5)操作系統版本升 級 ,之前的操作系統是基於CentOS6,至少需要適配CentOS 7 。
6)集群TPCH 壓測驗收 ,集群在完成部署之後,需要做一次整體的TPCH壓測驗收,如果存在明顯的問題需要不斷調整配置和架構,使得達到預期的性能目標。
此外在應用層面也有一些考慮,總而言之,是希望能夠解決絕大多數的痛點問題,無論是在系統層面,還是應用層面,都能上一個台階。
2.集群規劃設計的選型和思考
明確了目標,就是拆分任務來規劃設計了,在規劃設計方面主要有如下的幾個問題:
1)Greenplum的版本選擇 ,目前有兩個主要的版本類別,一個是開源版(Open Source distribution)和Pivotal官方版,它們的其中一個差異就是官方版需要注冊,簽署協議,在此基礎上還有GPCC等工具可以用,而開源版本可以實現源碼編譯或者rpm安裝,無法配置GPCC。綜合來看,我們選擇了 開源版本的6.16.2 ,這其中也詢問了一些行業朋友,特意選擇了幾個涉及穩定性bug修復的版本。
2)數據集市的技術選型 ,在數據集市的技術選型方面起初我是比較堅持基於PostgreSQL的模式,而業務側是希望對於一些較為復雜的邏輯能夠通過GP去支撐,一來二去之後,加上我咨詢了一些行業朋友的意見,是可以選擇基於GP的方案,於是我們就抱著試一試的方式做了壓測,所以數據倉庫和和數據集市會是兩個不同規模體量的GP集群來支撐。
3)GP的容量規劃 ,因為之前的節點設計有些過度,所以在數量上我們做了縮減,每台伺服器部署12個segment節點,比如一共12台伺服器,其中有10台伺服器是Segment節點,每台上面部署了6個Primary,6個Mirror,另外2台部署了Master和Standby,就是即(6+6)*10+2,整體的配置情況類似下面的模式。
4)部署架構方案選型 ,部署架構想起來比較容易,但是落實起來有很多的考慮細節,起初考慮GP的Master和Standby節點如果混用還是能夠節省一些資源,所以設計的數據倉庫和數據集市的部署架構是這樣考慮的,但是從走入部署階段之後,很快就發現這種交叉部署的模式是不可行的,或者說有一些復雜度。
除此之外,在單個GP集群的部署架構層面,還有4類方案考慮。
方案1 :Master,Standby和segment混合部署
方案2 :Master,Standby和segment獨立部署,整個集群的節點數會少一些
方案3 :Segment獨立部署,Master,Standby虛擬機部署
方案4 :最小化單節點集群部署(這是數據集市最保底的方案)
這方面存在較大的發揮空間,而且總體來說這種驗證磨合的成本也相對比較高,實踐給我上了一課, 越是想走捷徑,越是會讓你走一些彎路 ,而且有些時候的優化其實我也不知道改怎麼往下走,感覺已經無路可走,所以上面這4種方案其實我們都做了相關的測試和驗證。
3.集群架構的詳細設計和實踐
1)設計詳細的部署架構圖
在整體規劃之上,我設計了如下的部署架構圖,每個伺服器節點有6個Primary,6個Mirror,伺服器兩兩映射。
2)內核參數優化
按照官方文檔的建議和具體的配置情況,我們對內核參數做了如下的配置:
vm.swappiness=10
vm.zone_reclaim_mode = 0
vm.dirty_expire_centisecs = 500
vm.dirty_writeback_centisecs = 100
vm.dirty_background_ratio = 0 # See System Memory
vm.dirty_ratio = 0
vm.dirty_background_bytes = 1610612736
vm.dirty_bytes = 4294967296
vm.min_free_kbytes = 3943084
vm.overcommit_memory=2
kernel.sem = 500 2048000 200 4096
4.集群部署步驟
1)首先是配置/etc/hosts,需要把所有節點的IP和主機名都整理出來。
2)配置用戶,很常規的步驟
groupadd gpadmin
useradd gpadmin -g gpadmin
passwd gpadmin
3)配置sysctl.conf和資源配置
4)使用rpm模式安裝
# yum install -y apr apr-util bzip2 krb5-devel zip
# rpm -ivh open-source-greenplum-db-6.16.2-rhel7-x86_64.rpm
5)配置兩個host文件,也是為了後面進行統一部署方便,在此建議先開啟gpadmin的sudo許可權,可以通過gpssh處理一些較為復雜的批量操作
6)通過gpssh-exkeys來打通ssh信任關系,這里需要吐槽這個ssh互信,埠還得是22,否則處理起來很麻煩,需要修改/etc/ssh/sshd_config文件
gpssh-exkeys -f hostlist
7)較為復雜的一步是打包master的Greenplum-db-6.16.2軟體,然後分發到各個segment機器中,整個過程涉及文件打包,批量傳輸和配置,可以藉助gpscp和gpssh,比如gpscp傳輸文件,如下的命令會傳輸到/tmp目錄下
gpscp -f /usr/local/greenplum-db/conf/hostlist /tmp/greenplum-db-6.16.2.tar.gz =:/tmp
或者說在每台伺服器上面直接rpm -ivh安裝也可以。
8)Master節點需要單獨配置相關的目錄,而Segment節點的目錄可以提前規劃好,比如我們把Primary和Mirror放在不同的分區。
mkdir -p /data1/gpdata/gpdatap1
mkdir -p /data1/gpdata/gpdatap2
mkdir -p /data2/gpdata/gpdatam1
mkdir -p /data2/gpdata/gpdatam2
9)整個過程里最關鍵的就是gpinitsystem_config配置了,因為Segment節點的ID配置和命名,埠區間都是根據一定的規則來動態生成的,所以對於目錄的配置需要額外注意。
10)部署GP集群最關鍵的命令是
gpinitsystem -c gpinitsystem_config -s 【standby_hostname】
其中文件gpinitsystem_config的主要內容如下:
MASTER_HOSTNAME=xxxx
declare -a DATA_DIRECTORY=(/data1/gpdata/gpdatap1 /data1/gpdata/gpdatap2 /data1/gpdata/gpdatap3 /data1/gpdata/gpdatap4 /data1/gpdata/gpdatap5 /data1/gpdata/gpdatap6)
TRUSTED_SHELL=ssh
declare -a MIRROR_DATA_DIRECTORY=(/data2/gpdata/gpdatam1 /data2/gpdata/gpdatam2 /data2/gpdata/gpdatam3 /data2/gpdata/gpdatam4 /data2/gpdata/gpdatam5 /data2/gpdata/gpdatam6)
MACHINE_LIST_FILE=/usr/local/greenplum-db/conf/seg_hosts
整個過程大約5分鍾~10分鍾以內會完成,在部署過程中建議要查看後端的日誌查看是否有異常,異常情況下的體驗不是很好,可能會白等。
5.集群部署問題梳理
集群部署中還是有很多細節的問題,太基礎的就不提了,基本上就是配置,目錄許可權等問題,我提另外幾個:
1) 資源配置問題 ,如果/etc/security/limits.conf的資源配置不足會在安裝時有如下的警告:
2) 網路問題 ,集群部署完成後可以正常操作,但是在查詢數據的時候會拋出錯誤,比如SQL是這樣的,看起來很簡單:select count(*) from customer,但是會拋出如下的錯誤:
這個問題的主要原因還是和防火牆配置相關,其實不光需要配置INPUT的許可權,還需要配置OUTPUT的許可權。
對於數據節點可以開放略大的許可權,如:
入口的配置:
-A INPUT -p all -s xxxxx -j ACCEPT
出口的配置:
-A OUTPUT -p all -s xxxxx -j ACCEPT
3)網路配置問題 ,這個問題比較詭異的是,報錯和上面是一樣的,但是在排除了防火牆配置後,select count(*) from customer;這樣的語句是可以執行的,但是執行的等待時間較長,比如表lineitem這表比較大,過億的數據量,,在10個物理節點時,查詢響應時間是10秒,但是4個物理節點,查詢響應時間是在90秒,總體刪感覺說不過去。
為了排查網路問題,使用gpcheckperf等工具也做過測試,4節點和10節點的基礎配置也是相同的。
gpcheckperf -f /usr/local/greenplum-db/conf/seg_hosts -r N -d /tmp
$ cat /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
#127.0.0.1 test-dbs-gp-128-230
xxxxx.128.238 test-dbs-gp-svr-128-238
xxxxx.128.239 test-dbs-gp-svr-128-239
其中127.0.0.1的這個配置在segment和Master,Standby混部的情況是存在問題的,修正後就沒問題了,這個關鍵的問題也是郭運凱同學發現的。
5.集群故障恢復的測試
集群的故障測試是本次架構設計中的重點內容,所以這一塊也是躍躍欲試。
整體上我們包含兩個場景,伺服器宕機修復後的集群恢復和伺服器不可用時的恢復方式。
第一種場景相對比較簡單,就是讓Segment節點重新加入集群,並且在集群層面將Primary和Mirror的角色互換,而第二種場景相對時間較長一些,主要原因是需要重構數據節點,這個代價基本就就是PG層面的數據恢復了,為了整個測試和恢復能夠完整模擬,我們採用了類似的恢復方式,比如宕機修復使用了伺服器重啟來替代,而伺服器不可用則使用了清理數據目錄,類似於一台新配置機器的模式。
1)伺服器宕機修復後集群恢復
select * from gp_segment_configuration where status!='u'
gprecoverseg -o ./recov
gprecoverseg -r
select * from gp_segment_configuration where status='u'
2)伺服器不可用時集群恢復
重構數據節點的過程中,總體來看網路帶寬還是使用很充分的。
select * from gp_segment_configuration where status='u'
select * from gp_segment_configuration where status='u' and role!=preferred_role;
gprecoverseg -r
select * from gp_segment_configuration where status='u' and role!=preferred_role;
經過測試,重啟節點到數據修復,近50G數據耗時3分鍾左右
6.集群優化問題梳理
1)部署架構優化和迭代
對於優化問題,是本次測試中尤其關注,而且爭議較多的部分。
首先在做完初步選型後,數倉體系的部署相對是比較順利的,採用的是第一套方案。
數據集市的集群部分因為節點相對較少,所以就選用了第二套方案
實際測試的過程,因為配置問題導致TPCH的結果沒有達到預期。
所以這個階段也產生了一些疑問和懷疑,一種就是折回第一種方案,但是節點數會少很多,要不就是第三種採用虛擬機的模式部署,最保底的方案則是單節點部署,當然這是最牽強的方案。
這個階段確實很難,而在上面提到的修復了配置之後,集群好像突然開悟了一般,性能表現不錯,很快就完成了100G和1T數據量的TPCH測試。
在後續的改造中,我們也嘗試了第三套方案,基於虛擬機的模式,通過測試發現,遠沒有我們預期的那麼理想,在同樣的數據節點下,Master和Standby採用物理機和虛擬機,性能差異非常大,這個是出乎我們預料的。比如同樣的SQL,方案3執行需要2秒,而方案2則需要80秒,這個差異我們對比了很多指標,最後我個人理解差異還是在網卡部分。
所以經過對比後,還是選擇了方案2的混合部署模式。
2)SQL性能優化的分析
此外整個過程的TPCH也為集群的性能表現提供了參考。比如方案2的混合部署模式下,有一條SQL需要18秒,但是相比同類型的集群,可能就只需要2秒鍾左右,這塊顯然是存在問題的。
在排除了系統配置,硬體配置的差異之後,經典的解決辦法還是查看執行計劃。
性能較差的SQL執行計劃:
# explain analyze select count(*)from customer;
QUERY PLAN
Aggregate (cost=0.00..431.00 rows=1 width=8) (actual time=24792.916..24792.916 rows=1 loops=1)
-> Gather Motion 36:1 (slice1; segments: 36) (cost=0.00..431.00 rows=1 width=1) (actual time=3.255..16489.394 rows=150000000 loops=1)
-> Seq Scan on customer (cost=0.00..431.00 rows=1 width=1) (actual time=0.780..1267.878 rows=4172607 loops=1)
Planning time: 4.466 ms
(slice0) Executor memory: 680K bytes.
(slice1) Executor memory: 218K bytes avg x 36 workers, 218K bytes max (seg0).
Memory used: 2457600kB
Optimizer: Pivotal Optimizer (GPORCA)
Execution time: 24832.611 ms
(9 rows)
Time: 24892.500 ms
性能較好的SQL執行計劃:
# explain analyze select count(*)from customer;
QUERY PLAN
Aggregate (cost=0.00..842.08 rows=1 width=8) (actual time=1519.311..1519.311 rows=1 loops=1)
-> Gather Motion 36:1 (slice1; segments: 36) (cost=0.00..842.08 rows=1 width=8) (actual time=634.787..1519.214 rows=36 loops=1)
-> Aggregate (cost=0.00..842.08 rows=1 width=8) (actual time=1473.296..1473.296 rows=1 loops=1)
-> Seq Scan on customer (cost=0.00..834.33 rows=4166667 width=1) (actual time=0.758..438.319 rows=4172607 loops=1)
Planning time: 5.033 ms
(slice0) Executor memory: 176K bytes.
(slice1) Executor memory: 234K bytes avg x 36 workers, 234K bytes max (seg0).
Memory used: 2457600kB
Optimizer: Pivotal Optimizer (GPORCA)
Execution time: 1543.611 ms
(10 rows)
Time: 1549.324 ms
很明顯執行計劃是被誤導了,而誤導的因素則是基於統計信息,這個問題的修復很簡單:
analyze customer;
但是深究原因,則是在壓測時,先是使用了100G壓測,壓測完之後保留了原來的表結構,直接導入了1T的數據量,導致執行計劃這塊沒有更新。
3)集群配置優化
此外也做了一些集群配置層面的優化,比如對緩存做了調整。
gpconfig -c statement_mem -m 2457600 -v 2457600
gpconfig -c gp_vmem_protect_limit -m 32000 -v 32000
7.集群優化數據
最後來感受下集群的性能:
1)10個物理節點,(6+6)*10+2
tpch_1t=# iming on
Timing is on.
tpch_1t=# select count(*)from customer;
count
-----------
150000000
(1 row)
Time: 1235.801 ms
tpch_1t=# select count(*)from lineitem;
count
------------
5999989709
(1 row)
Time: 10661.756 ms
2)6個物理節點,(6+6)*6
# select count(*)from customer;
count
-----------
150000000
(1 row)
Time: 1346.833 ms
# select count(*)from lineitem;
count
------------
5999989709
(1 row)
Time: 18145.092 ms
3)4個物理節點,(6+6)*4
# select count(*)from customer;
count
-----------
150000000
(1 row)
Time: 1531.621 ms
# select count(*)from lineitem;
count
------------
5999989709
(1 row)
Time: 25072.501 ms
4)TPCH在不通架構模式下的性能比對 ,有19個查詢模型,有個別SQL邏輯過於復雜暫時忽略,也是郭運凱同學整理的列表。
在1T基準下的基準測試表現: