Äú¿ÉÒÔ¾èÖú£¬Ö§³ÖÎÒÃǵĹ«ÒæÊÂÒµ¡£

1Ôª 10Ôª 50Ôª





ÈÏÖ¤Â룺  ÑéÖ¤Âë,¿´²»Çå³þ?Çëµã»÷Ë¢ÐÂÑéÖ¤Âë ±ØÌî



  ÇóÖª ÎÄÕ ÎÄ¿â Lib ÊÓÆµ iPerson ¿Î³Ì ÈÏÖ¤ ×Éѯ ¹¤¾ß ½²×ù Model Center   Code  
»áÔ±   
   
 
     
   
 ¶©ÔÄ
  ¾èÖú
½â¾öSparkÊý¾ÝÇãб£¨Data Skew£©µÄNÖÖ×ËÊÆ
 
À´Ô´£ºInfoQ ·¢²¼ÓÚ£º2017-5-24
  2731  次浏览      27
 

ΪºÎÒª´¦ÀíÊý¾ÝÇãб£¨Data Skew£©

ʲôÊÇÊý¾ÝÇãб

¶ÔSpark/HadoopÕâÑùµÄ´óÊý¾ÝϵͳÀ´½²£¬Êý¾ÝÁ¿´ó²¢²»¿ÉÅ£¬¿ÉŵÄÊÇÊý¾ÝÇãб¡£

ºÎνÊý¾ÝÇãб£¿Êý¾ÝÇãбָµÄÊÇ£¬²¢Ðд¦ÀíµÄÊý¾Ý¼¯ÖУ¬Ä³Ò»²¿·Ö£¨ÈçSpark»òKafkaµÄÒ»¸öPartition£©µÄÊý¾ÝÏÔÖø¶àÓÚÆäËü²¿·Ö£¬´Ó¶øÊ¹µÃ¸Ã²¿·ÖµÄ´¦ÀíËٶȳÉΪÕû¸öÊý¾Ý¼¯´¦ÀíµÄÆ¿¾±¡£

Êý¾ÝÇãбÊÇÈçºÎÔì³ÉµÄ

ÔÚSparkÖУ¬Í¬Ò»¸öStageµÄ²»Í¬Partition¿ÉÒÔ²¢Ðд¦Àí£¬¶ø¾ßÌåÒÀÀµ¹ØÏµµÄ²»Í¬StageÖ®¼äÊÇ´®Ðд¦ÀíµÄ¡£¼ÙÉèij¸öSpark Job·ÖΪStage 0ºÍStage 1Á½¸öStage£¬ÇÒStage 1ÒÀÀµÓÚStage 0£¬ÄÇStage 0ÍêÈ«´¦Àí½áÊøÖ®Ç°²»»á´¦ÀíStage 1¡£¶øStage 0¿ÉÄܰüº¬N¸öTask£¬ÕâN¸öTask¿ÉÒÔ²¢ÐнøÐС£Èç¹ûÆäÖÐN-1¸öTask¶¼ÔÚ10ÃëÄÚÍê³É£¬¶øÁíÍâÒ»¸öTaskÈ´ºÄʱ1·ÖÖÓ£¬ÄǸÃStageµÄ×Üʱ¼äÖÁÉÙΪ1·ÖÖÓ¡£»»¾ä»°Ëµ£¬Ò»¸öStageËùºÄ·ÑµÄʱ¼ä£¬Ö÷ÒªÓÉ×îÂýµÄÄǸöTask¾ö¶¨¡£

ÓÉÓÚͬһ¸öStageÄÚµÄËùÓÐTaskÖ´ÐÐÏàͬµÄ¼ÆË㣬ÔÚÅųý²»Í¬¼ÆËã½Úµã¼ÆËãÄÜÁ¦²îÒìµÄǰÌáÏ£¬²»Í¬TaskÖ®¼äºÄʱµÄ²îÒìÖ÷ÒªÓɸÃTaskËù´¦ÀíµÄÊý¾ÝÁ¿¾ö¶¨¡£

StageµÄÊý¾ÝÀ´Ô´Ö÷Òª·ÖΪÈçÏÂÁ½Àà

  • ´ÓÊý¾ÝÔ´Ö±½Ó¶ÁÈ¡¡£Èç¶ÁÈ¡HDFS£¬Kafka
  • ¶ÁÈ¡ÉÏÒ»¸öStageµÄShuffleÊý¾Ý

ÈçºÎ»º½â/Ïû³ýÊý¾ÝÇãб

¾¡Á¿±ÜÃâÊý¾ÝÔ´µÄÊý¾ÝÇãб

ÒÔSpark Streamͨ¹ýDirectStream·½Ê½¶ÁÈ¡KafkaÊý¾ÝΪÀý¡£ÓÉÓÚKafkaµÄÿһ¸öPartition¶ÔÓ¦SparkµÄÒ»¸öTask£¨Partition£©£¬ËùÒÔKafkaÄÚÏà¹ØTopicµÄ¸÷PartitionÖ®¼äÊý¾ÝÊÇ·ñƽºâ£¬Ö±½Ó¾ö¶¨Spark´¦Àí¸ÃÊý¾ÝʱÊÇ·ñ»á²úÉúÊý¾ÝÇãб¡£

Èç¡¶KafkaÉè¼Æ½âÎö£¨Ò»£©- Kafka±³¾°¼°¼Ü¹¹½éÉÜ¡·Ò»ÎÄËùÊö£¬KafkaijһTopicÄÚÏûÏ¢ÔÚ²»Í¬PartitionÖ®¼äµÄ·Ö²¼£¬Ö÷ÒªÓÉProducer¶ËËùʹÓõÄPartitionʵÏÖÀà¾ö¶¨¡£Èç¹ûʹÓÃËæ»úPartitioner£¬ÔòÿÌõÏûÏ¢»áËæ»ú·¢Ë͵½Ò»¸öPartitionÖУ¬´Ó¶ø´Ó¸ÅÂÊÉÏÀ´½²£¬¸÷Partition¼äµÄÊý¾Ý»á´ïµ½Æ½ºâ¡£´ËʱԴStage£¨Ö±½Ó¶ÁÈ¡KafkaÊý¾ÝµÄStage£©²»»á²úÉúÊý¾ÝÇãб¡£

µ«ºÜ¶àʱºò£¬ÒµÎñ³¡¾°¿ÉÄÜ»áÒªÇ󽫾߱¸Í¬Ò»ÌØÕ÷µÄÊý¾Ý˳ÐòÏû·Ñ£¬´Ëʱ¾ÍÐèÒª½«¾ßÓÐÏàÍ¬ÌØÕ÷µÄÊý¾Ý·ÅÓÚͬһ¸öPartitionÖС£Ò»¸öµäÐ͵ij¡¾°ÊÇ£¬ÐèÒª½«Í¬Ò»¸öÓû§Ïà¹ØµÄPVÐÅÏ¢ÖÃÓÚͬһ¸öPartitionÖС£´Ëʱ£¬Èç¹û²úÉúÁËÊý¾ÝÇãб£¬ÔòÐèҪͨ¹ýÆäËü·½Ê½´¦Àí¡£

µ÷Õû²¢ÐжȷÖɢͬһ¸öTaskµÄ²»Í¬Key

Ô­Àí

SparkÔÚ×öShuffleʱ£¬Ä¬ÈÏʹÓÃHashPartitioner£¨·ÇHash Shuffle£©¶ÔÊý¾Ý½øÐзÖÇø¡£Èç¹û²¢ÐжÈÉèÖõIJ»ºÏÊÊ£¬¿ÉÄÜÔì³É´óÁ¿²»ÏàͬµÄKey¶ÔÓ¦µÄÊý¾Ý±»·ÖÅäµ½ÁËͬһ¸öTaskÉÏ£¬Ôì³É¸ÃTaskËù´¦ÀíµÄÊý¾ÝÔ¶´óÓÚÆäËüTask£¬´Ó¶øÔì³ÉÊý¾ÝÇãб¡£

Èç¹ûµ÷ÕûShuffleʱµÄ²¢Ðжȣ¬Ê¹µÃÔ­±¾±»·ÖÅ䵽ͬһTaskµÄ²»Í¬Key·¢Åäµ½²»Í¬TaskÉÏ´¦Àí£¬Ôò¿É½µµÍÔ­TaskËùÐè´¦ÀíµÄÊý¾ÝÁ¿£¬´Ó¶ø»º½âÊý¾ÝÇãбÎÊÌâÔì³ÉµÄ¶Ì°åЧӦ¡£

°¸Àý

ÏÖÓÐÒ»ÕŲâÊÔ±í£¬ÃûΪstudent_external£¬ÄÚÓÐ10.5ÒÚÌõÊý¾Ý£¬Ã¿ÌõÊý¾ÝÓÐÒ»¸öΨһµÄidÖµ¡£ÏÖ´ÓÖÐÈ¡³öidȡֵΪ9ÒÚµ½10.5ÒڵĹ²1.5ÌõÊý¾Ý£¬²¢Í¨¹ýһЩ´¦Àí£¬Ê¹µÃidΪ9ÒÚµ½9.4ÒÚ¼äµÄËùÓÐÊý¾Ý¶Ô12ȡģºóÓàÊýΪ8£¨¼´ÔÚShuffle²¢ÐжÈΪ12ʱ¸ÃÊý¾Ý¼¯È«²¿±»HashPartition·ÖÅäµ½µÚ8¸öTask£©£¬ÆäËüÊý¾Ý¼¯¶ÔÆäid³ýÒÔ100È¡Õû£¬´Ó¶øÊ¹µÃid´óÓÚ9.4ÒÚµÄÊý¾ÝÔÚShuffleʱ¿É±»¾ùÔÈ·ÖÅäµ½ËùÓÐTaskÖУ¬¶øidСÓÚ9.4ÒÚµÄÊý¾ÝÈ«²¿·ÖÅ䵽ͬһ¸öTaskÖС£´¦Àí¹ý³ÌÈçÏÂ

INSERT OVERWRITE TABLE test
SELECT CASE WHEN id < 940000000 THEN (9500000 + (CAST (RAND() * 8 AS INTEGER)) * 12 )
ELSE CAST(id/100 AS INTEGER)
END,
name
FROM student_external
WHERE id BETWEEN 900000000 AND 1050000000;

ͨ¹ýÉÏÊö´¦Àí£¬Ò»·Ý¿ÉÄÜÔì³ÉºóÐøÊý¾ÝÇãбµÄ²âÊÔÊý¾Ý¼´ÒÔ×¼±¸ºÃ¡£½ÓÏÂÀ´£¬Ê¹ÓÃSpark¶ÁÈ¡¸Ã²âÊÔÊý¾Ý£¬²¢Í¨¹ýgroupByKey(12)¶Ôid·Ö×é´¦Àí£¬ÇÒShuffle²¢ÐжÈΪ12¡£´úÂëÈçÏÂ

public class SparkDataSkew {
public static void main(String[] args) {
SparkSession sparkSession = SparkSession.builder()
.appName("SparkDataSkewTunning")
.config("hive.metastore.uris", "thrift://hadoop1:9083")
.enableHiveSupport()
.getOrCreate();
Dataset<Row> dataframe = sparkSession.sql( "select * from test");
dataframe.toJavaRDD()
.mapToPair((Row row) -> new Tuple2<Integer, String>(row.getInt(0),row.getString(1)))
.groupByKey(12)
.mapToPair((Tuple2<Integer, Iterable<String>> tuple) -> {
int id = tuple._1();
AtomicInteger atomicInteger = new AtomicInteger(0);
tuple._2().forEach((String name) -> atomicInteger.incrementAndGet());
return new Tuple2<Integer, Integer>(id, atomicInteger.get());
}).count();
sparkSession.stop();
sparkSession.close();
}
}

±¾´ÎʵÑéËùʹÓü¯Èº½ÚµãÊýΪ4£¬Ã¿¸ö½Úµã¿É±»YarnʹÓõÄCPUºËÊýΪ16£¬ÄÚ´æÎª16GB¡£Ê¹ÓÃÈçÏ·½Ê½Ìá½»ÉÏÊöÓ¦Ó㬽«Æô¶¯4¸öExecutor£¬Ã¿¸öExecutor¿ÉʹÓúËÊýΪ12£¨¸ÃÅäÖò¢·ÇÉú²ú»·¾³ÏµÄ×îÓÅÅäÖ㬽öÓÃÓÚ±¾ÎÄʵÑ飩£¬¿ÉÓÃÄÚ´æÎª12GB¡£

spark-submit --queue ambari --num-executors 4 --executor-cores 12 --executor-memory 12g --class com.jasongj.spark.driver.SparkDataSkew --master yarn --deploy-mode client SparkExample-with-dependencies-1.0.jar

GroupBy StageµÄTask״̬ÈçÏÂͼËùʾ£¬Task 8´¦ÀíµÄ¼Ç¼ÊýΪ4500Íò£¬Ô¶´óÓÚ£¨9±¶ÓÚ£©ÆäËü11¸öTask´¦ÀíµÄ500Íò¼Ç¼¡£¶øTask 8ËùºÄ·ÑµÄʱ¼äΪ38Ã룬Զ¸ßÓÚÆäËü11¸öTaskµÄƽ¾ùʱ¼ä£¨16Ã룩¡£Õû¸öStageµÄʱ¼äҲΪ38Ã룬¸Ãʱ¼äÖ÷ÒªÓÉ×îÂýµÄTask 8¾ö¶¨¡£

ÔÚÕâÖÖÇé¿öÏ£¬¿ÉÒÔͨ¹ýµ÷ÕûShuffle²¢Ðжȣ¬Ê¹µÃÔ­À´±»·ÖÅ䵽ͬһ¸öTask£¨¼´¸ÃÀýÖеÄTask 8£©µÄ²»Í¬Key·ÖÅäµ½²»Í¬Task£¬´Ó¶ø½µµÍTask 8ËùÐè´¦ÀíµÄÊý¾ÝÁ¿£¬»º½âÊý¾ÝÇãб¡£

ͨ¹ýgroupByKey(48)½«Shuffle²¢Ðжȵ÷ÕûΪ48£¬ÖØÐÂÌá½»µ½Spark¡£ÐµÄJobµÄGroupBy StageËùÓÐTask״̬ÈçÏÂͼËùʾ¡£

´ÓÉÏͼ¿ÉÖª£¬¼Ç¼Êý×î¶àµÄTask 20´¦ÀíµÄ¼Ç¼ÊýԼΪ1125Íò£¬Ïà±ÈÓÚ²¢ÐжÈΪ12ʱTask 8µÄ4500Íò£¬½µµÍÁË75%×óÓÒ£¬¶øÆäºÄʱ´ÓÔ­À´Task 8µÄ38Ãë½µµ½ÁË24Ãë¡£

ÔÚÕâÖÖ³¡¾°Ï£¬µ÷Õû²¢Ðжȣ¬²¢²»Òâζ×ÅÒ»¶¨ÒªÔö¼Ó²¢Ðжȣ¬Ò²¿ÉÄÜÊǼõС²¢Ðжȡ£Èç¹ûͨ¹ýgroupByKey(11)½«Shuffle²¢Ðжȵ÷ÕûΪ11£¬ÖØÐÂÌá½»µ½Spark¡£ÐÂJobµÄGroupBy StageµÄËùÓÐTask״̬ÈçÏÂͼËùʾ¡£

´ÓÉÏͼ¿É¼û£¬´¦Àí¼Ç¼Êý×î¶àµÄTask 6Ëù´¦ÀíµÄ¼Ç¼ÊýԼΪ1045Íò£¬ºÄʱΪ23Ãë¡£´¦Àí¼Ç¼Êý×îÉÙµÄTask 1´¦ÀíµÄ¼Ç¼ÊýԼΪ545Íò£¬ºÄʱ12Ãë¡£

×ܽá

ÊÊÓó¡¾°

´óÁ¿²»Í¬µÄKey±»·ÖÅäµ½ÁËÏàͬµÄTaskÔì³É¸ÃTaskÊý¾ÝÁ¿¹ý´ó¡£

½â¾ö·½°¸

µ÷Õû²¢Ðжȡ£Ò»°ãÊÇÔö´ó²¢Ðжȣ¬µ«ÓÐʱÈç±¾Àý¼õС²¢ÐжÈÒ²¿É´ïµ½Ð§¹û¡£

ÓÅÊÆ

ʵÏÖ¼òµ¥£¬¿ÉÔÚÐèÒªShuffleµÄ²Ù×÷Ëã×ÓÉÏÖ±½ÓÉèÖò¢ÐжȻòÕßʹÓÃspark.default.parallelismÉèÖá£Èç¹ûÊÇSpark SQL£¬»¹¿Éͨ¹ýSET spark.sql.shuffle.partitions=[num_tasks]ÉèÖò¢Ðжȡ£¿ÉÓÃ×îСµÄ´ú¼Û½â¾öÎÊÌâ¡£Ò»°ãÈç¹û³öÏÖÊý¾ÝÇãб£¬¶¼¿ÉÒÔͨ¹ýÕâÖÖ·½·¨ÏÈÊÔÑ鼸´Î£¬Èç¹ûÎÊÌâδ½â¾ö£¬ÔÙ³¢ÊÔÆäËü·½·¨¡£

ÁÓÊÆ

ÊÊÓó¡¾°ÉÙ£¬Ö»Äܽ«·ÖÅ䵽ͬһTaskµÄ²»Í¬Key·ÖÉ¢¿ª£¬µ«¶ÔÓÚͬһKeyÇãбÑÏÖØµÄÇé¿ö¸Ã·½·¨²¢²»ÊÊÓᣲ¢ÇҸ÷½·¨Ò»°ãÖ»ÄÜ»º½âÊý¾ÝÇãб£¬Ã»Óг¹µ×Ïû³ýÎÊÌâ¡£´Óʵ¼ù¾­ÑéÀ´¿´£¬ÆäЧ¹ûÒ»°ã¡£

×Ô¶¨ÒåPartitioner

Ô­Àí

ʹÓÃ×Ô¶¨ÒåµÄPartitioner£¨Ä¬ÈÏΪHashPartitioner£©£¬½«Ô­±¾±»·ÖÅ䵽ͬһ¸öTaskµÄ²»Í¬Key·ÖÅäµ½²»Í¬Task¡£

°¸Àý

ÒÔÉÏÊöÊý¾Ý¼¯ÎªÀý£¬¼ÌÐø½«²¢·¢¶ÈÉèÖÃΪ12£¬µ«ÊÇÔÚgroupByKeyËã×ÓÉÏ£¬Ê¹ÓÃ×Ô¶¨ÒåµÄPartitioner£¨ÊµÏÖÈçÏ£©

.groupByKey(new Partitioner() {
@Override
public int numPartitions() {
return 12;
}
@Override
public int getPartition(Object key) {
int id = Integer.parseInt(key.toString());
if(id >= 9500000 && id <= 9500084 && ((id - 9500000) % 12) == 0) {
return (id - 9500000) / 12;
} else {
return id % 12;
}
}
})

ÓÉÏÂͼ¿É¼û£¬Ê¹ÓÃ×Ô¶¨ÒåPartitionºó£¬ºÄʱ×µÄTask 6´¦ÀíÔ¼1000ÍòÌõÊý¾Ý£¬ÓÃʱ15Ãë¡£²¢ÇÒ¸÷TaskËù´¦ÀíµÄÊý¾Ý¼¯´óСÏ൱¡£

×ܽá

ÊÊÓó¡¾°

´óÁ¿²»Í¬µÄKey±»·ÖÅäµ½ÁËÏàͬµÄTaskÔì³É¸ÃTaskÊý¾ÝÁ¿¹ý´ó¡£

½â¾ö·½°¸

ʹÓÃ×Ô¶¨ÒåµÄPartitionerʵÏÖÀà´úÌæÄ¬ÈϵÄHashPartitioner£¬¾¡Á¿½«ËùÓв»Í¬µÄKey¾ùÔÈ·ÖÅäµ½²»Í¬µÄTaskÖС£

ÓÅÊÆ

²»Ó°ÏìÔ­ÓеIJ¢ÐжÈÉè¼Æ¡£Èç¹û¸Ä±ä²¢Ðжȣ¬ºóÐøStageµÄ²¢ÐжÈÒ²»áĬÈϸı䣬¿ÉÄÜ»áÓ°ÏìºóÐøStage¡£

ÁÓÊÆ

ÊÊÓó¡¾°ÓÐÏÞ£¬Ö»Äܽ«²»Í¬Key·ÖÉ¢¿ª£¬¶ÔÓÚͬһKey¶ÔÓ¦Êý¾Ý¼¯·Ç³£´óµÄ³¡¾°²»ÊÊÓá£Ð§¹ûÓëµ÷Õû²¢ÐжÈÀàËÆ£¬Ö»ÄÜ»º½âÊý¾ÝÇãб¶ø²»ÄÜÍêÈ«Ïû³ýÊý¾ÝÇãб¡£¶øÇÒÐèÒª¸ù¾ÝÊý¾ÝÌØµã×Ô¶¨ÒåרÓõÄPartitioner£¬²»¹»Áé»î¡£

½«Reduce side Joinת±äΪMap side Join

Ô­Àí

ͨ¹ýSparkµÄBroadcast»úÖÆ£¬½«Reduce²àJoinת»¯ÎªMap²àJoin£¬±ÜÃâShuffle´Ó¶øÍêÈ«Ïû³ýShuffle´øÀ´µÄÊý¾ÝÇãб¡£

°¸Àý

ͨ¹ýÈçÏÂSQL´´½¨Ò»ÕžßÓÐÇãбKeyÇÒ×ܼǼÊýΪ1.5ÒڵĴó±ítest¡£

INSERT OVERWRITE TABLE test
SELECT CAST(CASE WHEN id < 980000000 THEN (95000000 + (CAST (RAND() * 4 AS INT) + 1) * 48 )
ELSE CAST(id/10 AS INT) END AS STRING),
name
FROM student_external
WHERE id BETWEEN 900000000 AND 1050000000;

ʹÓÃÈçÏÂSQL´´½¨Ò»ÕÅÊý¾Ý·Ö²¼¾ùÔÈÇÒ×ܼǼÊýΪ50ÍòµÄС±ítest_new¡£

INSERT OVERWRITE TABLE test_new
SELECT CAST(CAST(id/10 AS INT) AS STRING),
name
FROM student_delta_external
WHERE id BETWEEN 950000000 AND 950500000;

Ö±½Óͨ¹ýSpark Thrift ServerÌá½»ÈçÏÂSQL½«±ítestÓë±ítest_new½øÐÐJoin²¢½«Join½á¹û´æÓÚ±ítest_joinÖС£

INSERT OVERWRITE TABLE test_join
SELECT test_new.id, test_new.name
FROM test
JOIN test_new
ON test.id = test_new.id;

¸ÃSQL¶ÔÓ¦µÄDAGÈçÏÂͼËùʾ¡£´Ó¸Ãͼ¿É¼û£¬¸ÃÖ´Ðйý³Ì×ܹ²·ÖΪÈý¸öStage£¬Ç°Á½¸öÓÃÓÚ´ÓHiveÖжÁÈ¡Êý¾Ý£¬Í¬Ê±¶þÕß½øÐÐShuffle£¬Í¨¹ý×îºóÒ»¸öStage½øÐÐJoin²¢½«½á¹ûдÈë±ítest_joinÖС£

´ÓÏÂͼ¿É¼û£¬×î½üJoin Stage¸÷Task´¦ÀíµÄÊý¾ÝÇãбÑÏÖØ£¬´¦ÀíÊý¾ÝÁ¿×î´óµÄTaskºÄʱ7.1·ÖÖÓ£¬Ô¶¸ßÓÚÆäËüÎÞÊý¾ÝÇãбµÄTaskÔ¼2sÃëµÄºÄʱ¡£

½ÓÏÂÀ´£¬³¢ÊÔͨ¹ýBroadcastʵÏÖMap²àJoin¡£ÊµÏÖMap²àJoinµÄ·½·¨£¬²¢·ÇÖ±½Óͨ¹ýCACHE TABLE test_new½«Ð¡±ítest_new½øÐÐcache¡£ÏÖͨ¹ýÈçÏÂSQL½øÐÐJoin¡£

CACHE TABLE test_new;
INSERT OVERWRITE TABLE test_join
SELECT test_new.id, test_new.name
FROM test
JOIN test_new
ON test.id = test_new.id;

ͨ¹ýÈçÏÂDAGͼ¿É¼û£¬¸Ã²Ù×÷ÈÔ·ÖΪÈý¸öStage£¬ÇÒÈÔÈ»ÓÐShuffle´æÔÚ£¬Î¨Ò»²»Í¬µÄÊÇ£¬Ð¡±íµÄ¶ÁÈ¡²»ÔÙÖ±½ÓɨÃèHive±í£¬¶øÊÇɨÃèÄÚ´æÖлº´æµÄ±í¡£

²¢ÇÒÊý¾ÝÇãбÈÔÈ»´æÔÚ¡£ÈçÏÂͼËùʾ£¬×îÂýµÄTaskºÄʱΪ7.1·ÖÖÓ£¬Ô¶¸ßÓÚÆäËüTaskµÄÔ¼2Ãë¡£

ÕýÈ·µÄʹÓÃBroadcastʵÏÖMap²àJoinµÄ·½Ê½ÊÇ£¬Í¨¹ýSET spark.sql.autoBroadcastJoinThreshold=104857600;½«BroadcastµÄãÐÖµÉèÖõÃ×ã¹»´ó¡£

ÔÙ´Îͨ¹ýÈçÏÂSQL½øÐÐJoin¡£

SET spark.sql.autoBroadcastJoinThreshold=104857600;
INSERT OVERWRITE TABLE test_join
SELECT test_new.id, test_new.name
FROM test
JOIN test_new
ON test.id = test_new.id;

ͨ¹ýÈçÏÂDAGͼ¿É¼û£¬¸Ã·½°¸Ö»°üº¬Ò»¸öStage¡£

²¢ÇÒ´ÓÏÂͼ¿É¼û£¬¸÷TaskºÄʱÏ൱£¬ÎÞÃ÷ÏÔÊý¾ÝÇãбÏÖÏó¡£²¢ÇÒ×ܺÄʱΪ1.5·ÖÖÓ£¬Ô¶µÍÓÚReduce²àJoinµÄ7.3·ÖÖÓ¡£

×ܽá

ÊÊÓó¡¾°

²ÎÓëJoinµÄÒ»±ßÊý¾Ý¼¯×㹻С£¬¿É±»¼ÓÔØ½øDriver²¢Í¨¹ýBroadcast·½·¨¹ã²¥µ½¸÷¸öExecutorÖС£

ÓÅÊÆ

±ÜÃâÁËShuffle£¬³¹µ×Ïû³ýÁËÊý¾ÝÇãб²úÉúµÄÌõ¼þ£¬¿É¼«´óÌáÉýÐÔÄÜ¡£

ÁÓÊÆ

ÒªÇó²ÎÓëJoinµÄÒ»²àÊý¾Ý¼¯×㹻С£¬²¢ÇÒÖ÷ÒªÊÊÓÃÓÚJoinµÄ³¡¾°£¬²»ÊʺϾۺϵij¡¾°£¬ÊÊÓÃÌõ¼þÓÐÏÞ¡£

ΪskewµÄkeyÔö¼ÓËæ»úǰ/ºó׺

Ô­Àí

ΪÊý¾ÝÁ¿Ìرð´óµÄKeyÔö¼ÓËæ»úǰ/ºó׺£¬Ê¹µÃÔ­À´KeyÏàͬµÄÊý¾Ý±äΪKey²»ÏàͬµÄÊý¾Ý£¬´Ó¶øÊ¹ÇãбµÄÊý¾Ý¼¯·ÖÉ¢µ½²»Í¬µÄTaskÖУ¬³¹µ×½â¾öÊý¾ÝÇãбÎÊÌâ¡£JoinÁíÒ»ÔòµÄÊý¾ÝÖУ¬ÓëÇãбKey¶ÔÓ¦µÄ²¿·ÖÊý¾Ý£¬ÓëËæ»úǰ׺¼¯×÷µÑ¿¨¶û³Ë»ý£¬´Ó¶ø±£Ö¤ÎÞÂÛÊý¾ÝÇãб²àÇãбKeyÈçºÎ¼Óǰ׺£¬¶¼ÄÜÓëÖ®Õý³£Join¡£

°¸Àý

ͨ¹ýÈçÏÂSQL£¬½«idΪ9ÒÚµ½9.08ÒÚ¹²800ÍòÌõÊý¾ÝµÄidתΪ9500048»òÕß9500096£¬ÆäËüÊý¾ÝµÄid³ýÒÔ100È¡Õû¡£´Ó¶ø¸ÃÊý¾Ý¼¯ÖУ¬idΪ9500048ºÍ9500096µÄÊý¾Ý¸÷400Íò£¬ÆäËüid¶ÔÓ¦µÄÊý¾Ý¼Ç¼Êý¾ùΪ100Ìõ¡£ÕâЩÊý¾Ý´æÓÚÃûΪtestµÄ±íÖС£

¶ÔÓÚÁíÍâÒ»ÕÅС±ítest_new£¬È¡³ö50ÍòÌõÊý¾Ý£¬²¢½«id£¨µÝÔöÇÒΨһ£©³ýÒÔ100È¡Õû£¬Ê¹µÃËùÓÐid¶¼¶ÔÓ¦100ÌõÊý¾Ý¡£

INSERT OVERWRITE TABLE test
SELECT CAST(CASE WHEN id < 908000000 THEN (9500000 + (CAST (RAND() * 2 AS INT) + 1) * 48 )
ELSE CAST(id/100 AS INT) END AS STRING),
name
FROM student_external
WHERE id BETWEEN 900000000 AND 1050000000;
INSERT OVERWRITE TABLE test_new
SELECT CAST(CAST(id/100 AS INT) AS STRING),
name
FROM student_delta_external
WHERE id BETWEEN 950000000 AND 950500000;

ͨ¹ýÈçÏ´úÂ룬¶ÁÈ¡test±í¶ÔÓ¦µÄÎļþ¼ÐÄÚµÄÊý¾Ý²¢×ª»»ÎªJavaPairRDD´æÓÚleftRDDÖУ¬Í¬Ñù¶ÁÈ¡test±í¶ÔÓ¦µÄÊý¾Ý´æÓÚrightRDDÖС£Í¨¹ýRDDµÄjoinËã×Ó¶ÔleftRDDÓërightRDD½øÐÐJoin£¬²¢Ö¸¶¨²¢ÐжÈΪ48¡£

public class SparkDataSkew{
public static void main(String[] args) {
SparkConf sparkConf = new SparkConf();
sparkConf.setAppName("DemoSparkDataFrameWithSkewedBigTableDirect");
sparkConf.set("spark.default.parallelism", parallelism + "");
JavaSparkContext javaSparkContext = new JavaSparkContext(sparkConf);
JavaPairRDD<String, String> leftRDD = javaSparkContext.textFile("hdfs://hadoop1:8020/apps/hive/warehouse/default/test/")
.mapToPair((String row) -> {
String[] str = row.split(",");
return new Tuple2<String, String>(str[0], str[1]);
});
JavaPairRDD<String, String> rightRDD = javaSparkContext.textFile("hdfs://hadoop1:8020/apps/hive/warehouse/default/test_new/")
.mapToPair((String row) -> {
String[] str = row.split(",");
return new Tuple2<String, String>(str[0], str[1]);
});
leftRDD.join(rightRDD, parallelism)
.mapToPair((Tuple2<String, Tuple2<String, String>> tuple) -> new Tuple2<String, String>(tuple._1(), tuple._2()._2()))
.foreachPartition((Iterator<Tuple2<String, String>> iterator) -> {
AtomicInteger atomicInteger = new AtomicInteger();
iterator.forEachRemaining((Tuple2<String, String> tuple) -> atomicInteger.incrementAndGet());
});
javaSparkContext.stop();
javaSparkContext.close();
}
}

´ÓÏÂͼ¿É¿´³ö£¬Õû¸öJoinºÄʱ1·Ö54Ã룬ÆäÖÐJoin StageºÄʱ1.7·ÖÖÓ¡£

ͨ¹ý·ÖÎöJoin StageµÄËùÓÐTask¿ÉÖª£¬ÔÚÆäËüTaskËù´¦Àí¼Ç¼ÊýΪ192.71ÍòµÄͬʱTask 32µÄ´¦ÀíµÄ¼Ç¼ÊýΪ992.72Íò£¬¹ÊËüºÄʱΪ1.7·ÖÖÓ£¬Ô¶¸ßÓÚÆäËüTaskµÄÔ¼10Ãë¡£ÕâÓëÉÏÎÄ×¼±¸Êý¾Ý¼¯Ê±£¬½«idΪ9500048Ϊ9500096¶ÔÓ¦µÄÊý¾ÝÁ¿ÉèÖ÷dz£´ó£¬ÆäËüid¶ÔÓ¦µÄÊý¾Ý¼¯·Ç³£¾ùÔÈÏà·ûºÏ¡£

ÏÖͨ¹ýÈçϲÙ×÷£¬ÊµÏÖÇãбKeyµÄ·ÖÉ¢´¦Àí

½«leftRDDÖÐÇãбµÄkey£¨¼´9500048Óë9500096£©¶ÔÓ¦µÄÊý¾Ýµ¥¶À¹ýÂ˳öÀ´£¬ÇÒ¼ÓÉÏ1µ½24µÄËæ»úǰ׺£¬²¢½«Ç°×ºÓëÔ­Êý¾ÝÓöººÅ·Ö¸ô£¨ÒÔ·½±ãÖ®ºóÈ¥µôǰ׺£©Ðγɵ¥¶ÀµÄleftSkewRDD

½«rightRDDÖÐÇãбkey¶ÔÓ¦µÄÊý¾Ý³éÈ¡³öÀ´£¬²¢Í¨¹ýflatMap²Ù×÷½«¸ÃÊý¾Ý¼¯ÖÐÿÌõÊý¾Ý¾ùת»»Îª24ÌõÊý¾Ý£¨Ã¿Ìõ·Ö±ð¼ÓÉÏ1µ½24µÄËæ»úǰ׺£©£¬Ðγɵ¥¶ÀµÄrightSkewRDD

½«leftSkewRDDÓërightSkewRDD½øÐÐJoin£¬²¢½«²¢ÐжÈÉèÖÃΪ48£¬ÇÒÔÚJoin¹ý³ÌÖн«Ëæ»úǰ׺ȥµô£¬µÃµ½ÇãбÊý¾Ý¼¯µÄJoin½á¹ûskewedJoinRDD

½«leftRDDÖв»°üº¬ÇãбKeyµÄÊý¾Ý³éÈ¡³öÀ´×÷Ϊµ¥¶ÀµÄleftUnSkewRDD

¶ÔleftUnSkewRDDÓëԭʼµÄrightRDD½øÐÐJoin£¬²¢ÐжÈÒ²ÉèÖÃΪ48£¬µÃµ½Join½á¹ûunskewedJoinRDD

ͨ¹ýunionËã×Ó½«skewedJoinRDDÓëunskewedJoinRDD½øÐкϲ¢£¬´Ó¶øµÃµ½ÍêÕûµÄJoin½á¹û¼¯

¾ßÌåʵÏÖ´úÂëÈçÏÂ

public class SparkDataSkew{
public static void main(String[] args) {
int parallelism = 48;
SparkConf sparkConf = new SparkConf();
sparkConf.setAppName("SolveDataSkewWithRandomPrefix");
sparkConf.set("spark.default.parallelism", parallelism + "");
JavaSparkContext javaSparkContext = new JavaSparkContext(sparkConf);
JavaPairRDD<String, String> leftRDD = javaSparkContext.textFile("hdfs://hadoop1:8020/apps/hive/warehouse/default/test/")
.mapToPair((String row) -> {
String[] str = row.split(",");
return new Tuple2<String, String>(str[0], str[1]);
});
JavaPairRDD<String, String> rightRDD = javaSparkContext.textFile("hdfs://hadoop1:8020/apps/hive/warehouse/default/test_new/")
.mapToPair((String row) -> {
String[] str = row.split(",");
return new Tuple2<String, String>(str[0], str[1]);
});
String[] skewedKeyArray = new String[]{"9500048", "9500096"};
Set<String> skewedKeySet = new HashSet<String>();
List<String> addList = new ArrayList<String>();
for(int i = 1; i <=24; i++) {
addList.add(i + "");
}
for(String key : skewedKeyArray) {
skewedKeySet.add(key);
}
Broadcast<Set<String>> skewedKeys = javaSparkContext.broadcast(skewedKeySet);
Broadcast<List<String>> addListKeys = javaSparkContext.broadcast(addList);
JavaPairRDD<String, String> leftSkewRDD = leftRDD
.filter((Tuple2<String, String> tuple) -> skewedKeys.value().contains(tuple._1()))
.mapToPair((Tuple2<String, String> tuple) -> new Tuple2<String, String>((new Random().nextInt(24) + 1) + "," + tuple._1(), tuple._2()));
JavaPairRDD<String, String> rightSkewRDD = rightRDD.filter((Tuple2<String, String> tuple) -> skewedKeys.value().contains(tuple._1()))
.flatMapToPair((Tuple2<String, String> tuple) -> addListKeys.value().stream()
.map((String i) -> new Tuple2<String, String>( i + "," + tuple._1(), tuple._2()))
.collect(Collectors.toList())
.iterator()
);
JavaPairRDD<String, String> skewedJoinRDD = leftSkewRDD
.join(rightSkewRDD, parallelism)
.mapToPair((Tuple2<String, Tuple2<String, String>> tuple) -> new Tuple2<String, String>(tuple._1().split(",")[1], tuple._2()._2()));
JavaPairRDD<String, String> leftUnSkewRDD = leftRDD.filter((Tuple2<String, String> tuple) -> !skewedKeys.value().contains(tuple._1()));
JavaPairRDD<String, String> unskewedJoinRDD = leftUnSkewRDD.join(rightRDD, parallelism).mapToPair((Tuple2<String, Tuple2<String, String>> tuple) -> new Tuple2<String, String>(tuple._1(), tuple._2()._2()));
skewedJoinRDD.union(unskewedJoinRDD).foreachPartition((Iterator<Tuple2<String, String>> iterator) -> {
AtomicInteger atomicInteger = new AtomicInteger();
iterator.forEachRemaining((Tuple2<String, String> tuple) -> atomicInteger.incrementAndGet());
});
javaSparkContext.stop();
javaSparkContext.close();
}
}

´ÓÏÂͼ¿É¿´³ö£¬Õû¸öJoinºÄʱ58Ã룬ÆäÖÐJoin StageºÄʱ33Ãë¡£

ͨ¹ý·ÖÎöJoin StageµÄËùÓÐTask¿ÉÖª

ÓÉÓÚJoin·ÖÇãбÊý¾Ý¼¯JoinºÍ·ÇÇãбÊý¾Ý¼¯Join£¬¶ø¸÷JoinµÄ²¢ÐжȾùΪ48£¬¹Ê×ܵIJ¢ÐжÈΪ96
ÓÉÓÚÌá½»ÈÎÎñʱ£¬ÉèÖõÄExecutor¸öÊýΪ4£¬Ã¿¸öExecutorµÄcoreÊýΪ12£¬¹Ê¿ÉÓÃCoreÊýΪ48£¬ËùÒÔǰ48¸öTaskͬʱÆô¶¯£¨ÆäLaunchʱ¼äÏàͬ£©£¬ºó48¸öTaskµÄÆô¶¯Ê±¼ä¸÷²»Ïàͬ£¨µÈ´ýÇ°ÃæµÄTask½áÊø²Å¿ªÊ¼£©
ÓÉÓÚÇãбKey±»¼ÓÉÏËæ»úǰ׺£¬Ô­±¾ÏàͬµÄKey±äΪ²»Í¬µÄKey£¬±»·ÖÉ¢µ½²»Í¬µÄTask´¦Àí£¬¹ÊÔÚËùÓÐTaskÖУ¬Î´·¢ÏÖËù´¦ÀíÊý¾Ý¼¯Ã÷ÏÔ¸ßÓÚÆäËüTaskµÄÇé¿ö

ʵ¼ÊÉÏ£¬ÓÉÓÚÇãбKeyÓë·ÇÇãбKeyµÄ²Ù×÷ÍêÈ«¶ÀÁ¢£¬¿É²¢ÐнøÐС£¶ø±¾ÊµÑéÊÜÏÞÓÚ¿ÉÓÃ×ܺËÊýΪ48£¬¿ÉͬʱÔËÐеÄ×ÜTaskÊýΪ48£¬¹Ê¶ø¸Ã·½°¸Ö»Êǽ«×ܺÄʱ¼õÉÙÒ»°ë£¨Ð§ÂÊÌáÉýÒ»±¶£©¡£Èç¹û×ÊÔ´³ä×㣬¿É²¢·¢Ö´ÐÐTaskÊýÔö¶à£¬¸Ã·½°¸µÄÓÅÊÆ½«¸üΪÃ÷ÏÔ¡£ÔÚʵ¼ÊÏîÄ¿ÖУ¬¸Ã·½°¸ÍùÍù¿ÉÌáÉýÊý±¶ÖÁ10±¶µÄЧÂÊ¡£

×ܽá

ÊÊÓó¡¾°

Á½ÕÅ±í¶¼±È½Ï´ó£¬ÎÞ·¨Ê¹ÓÃMapÔòJoin¡£ÆäÖÐÒ»¸öRDDÓÐÉÙÊý¼¸¸öKeyµÄÊý¾ÝÁ¿¹ý´ó£¬ÁíÍâÒ»¸öRDDµÄKey·Ö²¼½ÏΪ¾ùÔÈ¡£

½â¾ö·½°¸

½«ÓÐÊý¾ÝÇãбµÄRDDÖÐÇãбKey¶ÔÓ¦µÄÊý¾Ý¼¯µ¥¶À³éÈ¡³öÀ´¼ÓÉÏËæ»úǰ׺£¬ÁíÍâÒ»¸öRDDÿÌõÊý¾Ý·Ö±ðÓëËæ»úǰ׺½áºÏÐγÉеÄRDD£¨Ï൱ÓÚ½«ÆäÊý¾ÝÔöµ½µ½Ô­À´µÄN±¶£¬N¼´ÎªËæ»úǰ׺µÄ×ܸöÊý£©£¬È»ºó½«¶þÕßJoin²¢È¥µôǰ׺¡£È»ºó½«²»°üº¬ÇãбKeyµÄÊ£ÓàÊý¾Ý½øÐÐJoin¡£×îºó½«Á½´ÎJoinµÄ½á¹û¼¯Í¨¹ýunionºÏ²¢£¬¼´¿ÉµÃµ½È«²¿Join½á¹û¡£

ÓÅÊÆ

Ïà¶ÔÓÚMapÔòJoin£¬¸üÄÜÊÊÓ¦´óÊý¾Ý¼¯µÄJoin¡£Èç¹û×ÊÔ´³ä×㣬Çãб²¿·ÖÊý¾Ý¼¯Óë·ÇÇãб²¿·ÖÊý¾Ý¼¯¿É²¢ÐнøÐУ¬Ð§ÂÊÌáÉýÃ÷ÏÔ¡£ÇÒÖ»Õë¶ÔÇãб²¿·ÖµÄÊý¾Ý×öÊý¾ÝÀ©Õ¹£¬Ôö¼ÓµÄ×ÊÔ´ÏûºÄÓÐÏÞ¡£

ÁÓÊÆ

Èç¹ûÇãбKey·Ç³£¶à£¬ÔòÁíÒ»²àÊý¾ÝÅòÕͷdz£´ó£¬´Ë·½°¸²»ÊÊÓ᣶øÇÒ´Ëʱ¶ÔÇãбKeyÓë·ÇÇãбKey·Ö¿ª´¦Àí£¬ÐèҪɨÃèÊý¾Ý¼¯Á½±é£¬Ôö¼ÓÁË¿ªÏú¡£

´ó±íËæ»úÌí¼ÓNÖÖËæ»úǰ׺£¬Ð¡±íÀ©´óN±¶

Ô­Àí

Èç¹û³öÏÖÊý¾ÝÇãбµÄKey±È½Ï¶à£¬ÉÏÒ»ÖÖ·½·¨½«ÕâЩ´óÁ¿µÄÇãбKey·Ö²ð³öÀ´£¬ÒâÒå²»´ó¡£´Ëʱ¸üÊʺÏÖ±½Ó¶Ô´æÔÚÊý¾ÝÇãбµÄÊý¾Ý¼¯È«²¿¼ÓÉÏËæ»úǰ׺£¬È»ºó¶ÔÁíÍâÒ»¸ö²»´æÔÚÑÏÖØÊý¾ÝÇãбµÄÊý¾Ý¼¯ÕûÌåÓëËæ»úǰ׺¼¯×÷µÑ¿¨¶û³Ë»ý£¨¼´½«Êý¾ÝÁ¿À©´óN±¶£©¡£

°¸Àý

ÕâÀï¸ø³öʾÀý´úÂ룬¶ÁÕ߿ɲο¼ÉÏÎÄÖзֲð³öÉÙÊýÇãбKeyÌí¼ÓËæ»úǰ׺µÄ·½·¨£¬×ÔÐвâÊÔ¡£

public class SparkDataSkew {
public static void main(String[] args) {
SparkConf sparkConf = new SparkConf();
sparkConf.setAppName("ResolveDataSkewWithNAndRandom");
sparkConf.set("spark.default.parallelism", parallelism + "");
JavaSparkContext javaSparkContext = new JavaSparkContext(sparkConf);
JavaPairRDD<String, String> leftRDD = javaSparkContext.textFile("hdfs://hadoop1:8020/apps/hive/warehouse/default/test/")
.mapToPair((String row) -> {
String[] str = row.split(",");
return new Tuple2<String, String>(str[0], str[1]);
});
JavaPairRDD<String, String> rightRDD = javaSparkContext.textFile("hdfs://hadoop1:8020/apps/hive/warehouse/default/test_new/")
.mapToPair((String row) -> {
String[] str = row.split(",");
return new Tuple2<String, String>(str[0], str[1]);
});
List<String> addList = new ArrayList<String>();
for(int i = 1; i <=48; i++) {
addList.add(i + "");
}
Broadcast<List<String>> addListKeys = javaSparkContext.broadcast(addList);
JavaPairRDD<String, String> leftRandomRDD = leftRDD.mapToPair((Tuple2<String, String> tuple) -> new Tuple2<String, String>(new Random().nextInt(48) + "," + tuple._1(), tuple._2()));
JavaPairRDD<String, String> rightNewRDD = rightRDD
.flatMapToPair((Tuple2<String, String> tuple) -> addListKeys.value().stream()
.map((String i) -> new Tuple2<String, String>( i + "," + tuple._1(), tuple._2()))
.collect(Collectors.toList())
.iterator()
);
JavaPairRDD<String, String> joinRDD = leftRandomRDD
.join(rightNewRDD, parallelism)
.mapToPair((Tuple2<String, Tuple2<String, String>> tuple) -> new Tuple2<String, String>(tuple._1().split(",")[1], tuple._2()._2()));
joinRDD.foreachPartition((Iterator<Tuple2<String, String>> iterator) -> {
AtomicInteger atomicInteger = new AtomicInteger();
iterator.forEachRemaining((Tuple2<String, String> tuple) -> atomicInteger.incrementAndGet());
});
javaSparkContext.stop();
javaSparkContext.close();
}
}

×ܽá

ÊÊÓó¡¾°

Ò»¸öÊý¾Ý¼¯´æÔÚµÄÇãбKey±È½Ï¶à£¬ÁíÍâÒ»¸öÊý¾Ý¼¯Êý¾Ý·Ö²¼±È½Ï¾ùÔÈ¡£

ÓÅÊÆ

¶Ô´ó²¿·Ö³¡¾°¶¼ÊÊÓã¬Ð§¹û²»´í¡£

ÁÓÊÆ

ÐèÒª½«Ò»¸öÊý¾Ý¼¯ÕûÌåÀ©´óN±¶£¬»áÔö¼Ó×ÊÔ´ÏûºÄ¡£

×ܽá

¶ÔÓÚÊý¾ÝÇãб£¬²¢ÎÞÒ»¸öͳһµÄÒ»ÀÍÓÀÒݵķ½·¨¡£¸ü¶àµÄʱºò£¬ÊǽáºÏÊý¾ÝÌØµã£¨Êý¾Ý¼¯´óС£¬ÇãбKeyµÄ¶àÉٵȣ©×ÛºÏʹÓÃÉÏÎÄËùÊöµÄ¶àÖÖ·½·¨¡£

   
2731 ´Îä¯ÀÀ       27
Ïà¹ØÎÄÕÂ

»ùÓÚEAµÄÊý¾Ý¿â½¨Ä£
Êý¾ÝÁ÷½¨Ä££¨EAÖ¸ÄÏ£©
¡°Êý¾Ýºþ¡±£º¸ÅÄî¡¢ÌØÕ÷¡¢¼Ü¹¹Óë°¸Àý
ÔÚÏßÉ̳ÇÊý¾Ý¿âϵͳÉè¼Æ ˼·+Ч¹û
 
Ïà¹ØÎĵµ

GreenplumÊý¾Ý¿â»ù´¡Åàѵ
MySQL5.1ÐÔÄÜÓÅ»¯·½°¸
ijµçÉÌÊý¾ÝÖÐ̨¼Ü¹¹Êµ¼ù
MySQL¸ßÀ©Õ¹¼Ü¹¹Éè¼Æ
Ïà¹Ø¿Î³Ì

Êý¾ÝÖÎÀí¡¢Êý¾Ý¼Ü¹¹¼°Êý¾Ý±ê×¼
MongoDBʵս¿Î³Ì
²¢·¢¡¢´óÈÝÁ¿¡¢¸ßÐÔÄÜÊý¾Ý¿âÉè¼ÆÓëÓÅ»¯
PostgreSQLÊý¾Ý¿âʵսÅàѵ