[ 
https://issues.apache.org/jira/browse/PHOENIX-6944?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17718013#comment-17718013
 ] 

ASF GitHub Bot commented on PHOENIX-6944:
-----------------------------------------

stoty commented on code in PR #1596:
URL: https://github.com/apache/phoenix/pull/1596#discussion_r1181172959


##########
phoenix-core/src/main/java/org/apache/phoenix/mapreduce/PhoenixInputFormat.java:
##########
@@ -87,74 +88,106 @@ public List<InputSplit> getSplits(JobContext context) 
throws IOException, Interr
         return generateSplits(queryPlan, configuration);
     }
 
-    protected List<InputSplit> generateSplits(final QueryPlan qplan, 
Configuration config) throws IOException {
+    /**
+     * Randomise the length parameter of the splits to ensure random execution 
order.
+     * Yarn orders splits by size before execution.
+     *
+     * @param splits
+     */
+    protected void randomizeSplitLength(List<InputSplit> splits) {
+        LOGGER.info("Randomizing split size");
+        if (splits.size() == 0) {
+            return;
+        }
+        double defaultLength = 1000000d;
+        double totalLength = splits.stream().mapToDouble(s -> {
+            try {
+                return (double)s.getLength();
+            } catch (IOException | InterruptedException e1) {
+                return defaultLength;
+            }
+        }).sum();
+        long avgLength = (long) (totalLength / splits.size());
+        splits.stream().forEach(s -> ((PhoenixInputSplit) s)
+                .setLength(avgLength + 
ThreadLocalRandom.current().nextInt(10000)));
+    }
+
+    protected List<InputSplit> generateSplits(final QueryPlan qplan, 
Configuration config)
+            throws IOException {
         // We must call this in order to initialize the scans and splits from 
the query plan
         setupParallelScansFromQueryPlan(qplan);
         final List<KeyRange> splits = qplan.getSplits();
         Preconditions.checkNotNull(splits);
 
         // Get the RegionSizeCalculator
-        try(org.apache.hadoop.hbase.client.Connection connection =
-                    
HBaseFactoryProvider.getHConnectionFactory().createConnection(config)) {
-        RegionLocator regionLocator = 
connection.getRegionLocator(TableName.valueOf(qplan
-                .getTableRef().getTable().getPhysicalName().toString()));
-        RegionSizeCalculator sizeCalculator = new 
RegionSizeCalculator(regionLocator, connection
-                .getAdmin());
+        try (org.apache.hadoop.hbase.client.Connection connection =
+                
HBaseFactoryProvider.getHConnectionFactory().createConnection(config)) {
+            RegionLocator regionLocator =
+                    connection.getRegionLocator(TableName
+                            
.valueOf(qplan.getTableRef().getTable().getPhysicalName().toString()));
+            RegionSizeCalculator sizeCalculator =
+                    new RegionSizeCalculator(regionLocator, 
connection.getAdmin());
 
-        final List<InputSplit> psplits = 
Lists.newArrayListWithExpectedSize(splits.size());
-        for (List<Scan> scans : qplan.getScans()) {
-            // Get the region location
-            HRegionLocation location = regionLocator.getRegionLocation(
-                    scans.get(0).getStartRow(),
-                    false
-            );
+            final List<InputSplit> psplits = 
Lists.newArrayListWithExpectedSize(splits.size());
+            for (List<Scan> scans : qplan.getScans()) {
+                // Get the region location
+                HRegionLocation location =
+                        
regionLocator.getRegionLocation(scans.get(0).getStartRow(), false);
 
-            String regionLocation = location.getHostname();
+                String regionLocation = location.getHostname();
 
-            // Get the region size
-            long regionSize = sizeCalculator.getRegionSize(
-                    location.getRegion().getRegionName()
-            );
+                // Get the region size
+                long regionSize =
+                        
sizeCalculator.getRegionSize(location.getRegion().getRegionName());
 
-            // Generate splits based off statistics, or just region splits?
-            boolean splitByStats = 
PhoenixConfigurationUtil.getSplitByStats(config);
+                // Generate splits based off statistics, or just region splits?
+                boolean splitByStats = 
PhoenixConfigurationUtil.getSplitByStats(config);
 
-            if (splitByStats) {
-                for (Scan aScan : scans) {
-                    if (LOGGER.isDebugEnabled()) {
-                        LOGGER.debug("Split for  scan : " + aScan + "with 
scanAttribute : " + aScan
-                                .getAttributesMap() + " [scanCache, 
cacheBlock, scanBatch] : [" +
-                                aScan.getCaching() + ", " + 
aScan.getCacheBlocks() + ", " + aScan
-                                .getBatch() + "] and  regionLocation : " + 
regionLocation);
-                    }
+                if (splitByStats) {
+                    for (Scan aScan : scans) {
+                        if (LOGGER.isDebugEnabled()) {
+                            LOGGER.debug("Split for  scan : " + aScan + "with 
scanAttribute : "
+                                    + aScan.getAttributesMap()
+                                    + " [scanCache, cacheBlock, scanBatch] : 
[" + aScan.getCaching()
+                                    + ", " + aScan.getCacheBlocks() + ", " + 
aScan.getBatch()
+                                    + "] and  regionLocation : " + 
regionLocation);
+                        }
 
-                    psplits.add(new 
PhoenixInputSplit(Collections.singletonList(aScan), regionSize, 
regionLocation));
-                }
+                        // The size is bogus, but it's not a problem
+                        psplits.add(new 
PhoenixInputSplit(Collections.singletonList(aScan),
+                                regionSize, regionLocation));
+                    }
                 } else {
-                if (LOGGER.isDebugEnabled()) {
-                    LOGGER.debug("Scan count[" + scans.size() + "] : " + 
Bytes.toStringBinary(scans
-                            .get(0).getStartRow()) + " ~ " + 
Bytes.toStringBinary(scans.get(scans
-                            .size() - 1).getStopRow()));
-                    LOGGER.debug("First scan : " + scans.get(0) + "with 
scanAttribute : " + scans
-                            .get(0).getAttributesMap() + " [scanCache, 
cacheBlock, scanBatch] : " +
-                            "[" + scans.get(0).getCaching() + ", " + 
scans.get(0).getCacheBlocks()
-                            + ", " + scans.get(0).getBatch() + "] and  
regionLocation : " +
-                            regionLocation);
+                    if (LOGGER.isDebugEnabled()) {
+                        LOGGER.debug("Scan count[" + scans.size() + "] : "
+                                + 
Bytes.toStringBinary(scans.get(0).getStartRow()) + " ~ "
+                                + Bytes.toStringBinary(scans.get(scans.size() 
- 1).getStopRow()));
+                        LOGGER.debug("First scan : " + scans.get(0) + "with 
scanAttribute : "
+                                + scans.get(0).getAttributesMap()
+                                + " [scanCache, cacheBlock, scanBatch] : " + 
"["
+                                + scans.get(0).getCaching() + ", " + 
scans.get(0).getCacheBlocks()
+                                + ", " + scans.get(0).getBatch() + "] and  
regionLocation : "
+                                + regionLocation);
 
-                    for (int i = 0, limit = scans.size(); i < limit; i++) {
-                        LOGGER.debug("EXPECTED_UPPER_REGION_KEY[" + i + "] : " 
+ Bytes
-                                .toStringBinary(scans.get(i).getAttribute
-                                        
(BaseScannerRegionObserver.EXPECTED_UPPER_REGION_KEY)));
+                        for (int i = 0, limit = scans.size(); i < limit; i++) {
+                            LOGGER.debug("EXPECTED_UPPER_REGION_KEY[" + i + "] 
: "
+                                    + 
Bytes.toStringBinary(scans.get(i).getAttribute(
+                                        
BaseScannerRegionObserver.EXPECTED_UPPER_REGION_KEY)));
+                        }
                     }
+
+                    psplits.add(new PhoenixInputSplit(scans, regionSize, 
regionLocation));
                 }
+            }
 
-                psplits.add(new PhoenixInputSplit(scans, regionSize, 
regionLocation));
+            if 
(PhoenixConfigurationUtil.isMRRandomizeMapperExecutionOrder(config)) {

Review Comment:
   This is the only code change, the rest of the changes in thsis method are 
just reformats



##########
phoenix-core/src/main/java/org/apache/phoenix/mapreduce/PhoenixInputFormat.java:
##########
@@ -87,74 +88,106 @@ public List<InputSplit> getSplits(JobContext context) 
throws IOException, Interr
         return generateSplits(queryPlan, configuration);
     }
 
-    protected List<InputSplit> generateSplits(final QueryPlan qplan, 
Configuration config) throws IOException {
+    /**
+     * Randomise the length parameter of the splits to ensure random execution 
order.
+     * Yarn orders splits by size before execution.
+     *
+     * @param splits
+     */
+    protected void randomizeSplitLength(List<InputSplit> splits) {
+        LOGGER.info("Randomizing split size");
+        if (splits.size() == 0) {
+            return;
+        }
+        double defaultLength = 1000000d;
+        double totalLength = splits.stream().mapToDouble(s -> {
+            try {
+                return (double)s.getLength();
+            } catch (IOException | InterruptedException e1) {
+                return defaultLength;
+            }
+        }).sum();
+        long avgLength = (long) (totalLength / splits.size());
+        splits.stream().forEach(s -> ((PhoenixInputSplit) s)
+                .setLength(avgLength + 
ThreadLocalRandom.current().nextInt(10000)));
+    }
+
+    protected List<InputSplit> generateSplits(final QueryPlan qplan, 
Configuration config)
+            throws IOException {
         // We must call this in order to initialize the scans and splits from 
the query plan
         setupParallelScansFromQueryPlan(qplan);
         final List<KeyRange> splits = qplan.getSplits();
         Preconditions.checkNotNull(splits);
 
         // Get the RegionSizeCalculator
-        try(org.apache.hadoop.hbase.client.Connection connection =
-                    
HBaseFactoryProvider.getHConnectionFactory().createConnection(config)) {
-        RegionLocator regionLocator = 
connection.getRegionLocator(TableName.valueOf(qplan
-                .getTableRef().getTable().getPhysicalName().toString()));
-        RegionSizeCalculator sizeCalculator = new 
RegionSizeCalculator(regionLocator, connection
-                .getAdmin());
+        try (org.apache.hadoop.hbase.client.Connection connection =
+                
HBaseFactoryProvider.getHConnectionFactory().createConnection(config)) {
+            RegionLocator regionLocator =
+                    connection.getRegionLocator(TableName
+                            
.valueOf(qplan.getTableRef().getTable().getPhysicalName().toString()));
+            RegionSizeCalculator sizeCalculator =
+                    new RegionSizeCalculator(regionLocator, 
connection.getAdmin());
 
-        final List<InputSplit> psplits = 
Lists.newArrayListWithExpectedSize(splits.size());
-        for (List<Scan> scans : qplan.getScans()) {
-            // Get the region location
-            HRegionLocation location = regionLocator.getRegionLocation(
-                    scans.get(0).getStartRow(),
-                    false
-            );
+            final List<InputSplit> psplits = 
Lists.newArrayListWithExpectedSize(splits.size());
+            for (List<Scan> scans : qplan.getScans()) {
+                // Get the region location
+                HRegionLocation location =
+                        
regionLocator.getRegionLocation(scans.get(0).getStartRow(), false);
 
-            String regionLocation = location.getHostname();
+                String regionLocation = location.getHostname();
 
-            // Get the region size
-            long regionSize = sizeCalculator.getRegionSize(
-                    location.getRegion().getRegionName()
-            );
+                // Get the region size
+                long regionSize =
+                        
sizeCalculator.getRegionSize(location.getRegion().getRegionName());
 
-            // Generate splits based off statistics, or just region splits?
-            boolean splitByStats = 
PhoenixConfigurationUtil.getSplitByStats(config);
+                // Generate splits based off statistics, or just region splits?
+                boolean splitByStats = 
PhoenixConfigurationUtil.getSplitByStats(config);
 
-            if (splitByStats) {
-                for (Scan aScan : scans) {
-                    if (LOGGER.isDebugEnabled()) {
-                        LOGGER.debug("Split for  scan : " + aScan + "with 
scanAttribute : " + aScan
-                                .getAttributesMap() + " [scanCache, 
cacheBlock, scanBatch] : [" +
-                                aScan.getCaching() + ", " + 
aScan.getCacheBlocks() + ", " + aScan
-                                .getBatch() + "] and  regionLocation : " + 
regionLocation);
-                    }
+                if (splitByStats) {
+                    for (Scan aScan : scans) {
+                        if (LOGGER.isDebugEnabled()) {
+                            LOGGER.debug("Split for  scan : " + aScan + "with 
scanAttribute : "
+                                    + aScan.getAttributesMap()
+                                    + " [scanCache, cacheBlock, scanBatch] : 
[" + aScan.getCaching()
+                                    + ", " + aScan.getCacheBlocks() + ", " + 
aScan.getBatch()
+                                    + "] and  regionLocation : " + 
regionLocation);
+                        }
 
-                    psplits.add(new 
PhoenixInputSplit(Collections.singletonList(aScan), regionSize, 
regionLocation));
-                }
+                        // The size is bogus, but it's not a problem
+                        psplits.add(new 
PhoenixInputSplit(Collections.singletonList(aScan),
+                                regionSize, regionLocation));
+                    }
                 } else {
-                if (LOGGER.isDebugEnabled()) {
-                    LOGGER.debug("Scan count[" + scans.size() + "] : " + 
Bytes.toStringBinary(scans
-                            .get(0).getStartRow()) + " ~ " + 
Bytes.toStringBinary(scans.get(scans
-                            .size() - 1).getStopRow()));
-                    LOGGER.debug("First scan : " + scans.get(0) + "with 
scanAttribute : " + scans
-                            .get(0).getAttributesMap() + " [scanCache, 
cacheBlock, scanBatch] : " +
-                            "[" + scans.get(0).getCaching() + ", " + 
scans.get(0).getCacheBlocks()
-                            + ", " + scans.get(0).getBatch() + "] and  
regionLocation : " +
-                            regionLocation);
+                    if (LOGGER.isDebugEnabled()) {
+                        LOGGER.debug("Scan count[" + scans.size() + "] : "
+                                + 
Bytes.toStringBinary(scans.get(0).getStartRow()) + " ~ "
+                                + Bytes.toStringBinary(scans.get(scans.size() 
- 1).getStopRow()));
+                        LOGGER.debug("First scan : " + scans.get(0) + "with 
scanAttribute : "
+                                + scans.get(0).getAttributesMap()
+                                + " [scanCache, cacheBlock, scanBatch] : " + 
"["
+                                + scans.get(0).getCaching() + ", " + 
scans.get(0).getCacheBlocks()
+                                + ", " + scans.get(0).getBatch() + "] and  
regionLocation : "
+                                + regionLocation);
 
-                    for (int i = 0, limit = scans.size(); i < limit; i++) {
-                        LOGGER.debug("EXPECTED_UPPER_REGION_KEY[" + i + "] : " 
+ Bytes
-                                .toStringBinary(scans.get(i).getAttribute
-                                        
(BaseScannerRegionObserver.EXPECTED_UPPER_REGION_KEY)));
+                        for (int i = 0, limit = scans.size(); i < limit; i++) {
+                            LOGGER.debug("EXPECTED_UPPER_REGION_KEY[" + i + "] 
: "
+                                    + 
Bytes.toStringBinary(scans.get(i).getAttribute(
+                                        
BaseScannerRegionObserver.EXPECTED_UPPER_REGION_KEY)));
+                        }
                     }
+
+                    psplits.add(new PhoenixInputSplit(scans, regionSize, 
regionLocation));
                 }
+            }
 
-                psplits.add(new PhoenixInputSplit(scans, regionSize, 
regionLocation));
+            if 
(PhoenixConfigurationUtil.isMRRandomizeMapperExecutionOrder(config)) {

Review Comment:
   This is the only code change, the rest of the changes in this method are 
just reformats





> Randomize mapper task ordering for MR tools
> -------------------------------------------
>
>                 Key: PHOENIX-6944
>                 URL: https://issues.apache.org/jira/browse/PHOENIX-6944
>             Project: Phoenix
>          Issue Type: Improvement
>          Components: core
>            Reporter: Istvan Toth
>            Priority: Major
>              Labels: perf
>
> Currently, splits are generated by PhoenixInputFormat are in ascending order.
> MR does not use this ordering directly, it instead orders the partitions by 
> size in descending order.
> We set the sizes of the splits to the region size. (Even when splitting by 
> guideposts, but this not really a problem)
> The result is that mapper jobs are grouped by regions, so usually all the 
> mappers running are working on one, or few regions. As a result we have the 
> following problems:
> Read hotspotting:
> All scan operations for the indexing job hit the same one or few region 
> servers, causing high loads and slowdowns.
> Write hotspotting:
> If the data rowkeys and index rowkeys strongly correlate, then the data read 
> from one or few data regions will be written to one or few index regions, 
> causing high loads and slowdowns. This is a bit of a corner case, we have 
> obeserved it when building an index for a column which starts with the same 
> bytes as the primary key for the data table.
> We can improve this by making sure that the generate mapper jobs are executed 
> in a random order. The only way to change the execution order is to 
> manipulate the length of the splits. As the length is only used for ordering, 
> and calculating completion percentage, this is unlikely to cause problems (we 
> already specify wildly off lengths when splitting by guidepost )
> I've run some test on a 50M row, 40GB data table, generating secondary 
> indexes for a correlated field and for a random field:
> The test system has three RS workers, and 12 yarn slots for running IndexTool
> ||Index rebuild time||on correlated field||on random field||
> |w/o randomization|50 min|28 min|
> |w/ randomization|30 min|23 min|



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to