BUG: Preserve key order when using loc on MultiIndex DataFrame 
State: closed

## Description
closes #22797 
As described in #22797, the key order given to loc for a MultiIndex DataFrame was not respected:
```
import pandas as pd
import numpy as np
df = pd.DataFrame(np.arange(12).reshape((4, 3)),
    index=[['a', 'a', 'b', 'b'], [1, 2, 1, 2]],
    columns=[['Ohio', 'Ohio', 'Colorado'],
    ['Green', 'Red', 'Green']])

df.loc[(['b','a'],[2, 1]),:]

# Out
     Ohio     Colorado
    Green Red    Green
a 1     0   1        2
  2     3   4        5
b 1     6   7        8
  2     9  10       11
```

## Proposed fix
The culprit was the use of intersection of indexers in the loc function. I tried keeping the indexers sorted during the whole function (in the main loop), but performance were really affected (by a factor 3!!!).
As an other solution, I tried to sort the result after the indexers were computed. It was already way better (worse "only" by a factor 1.15 or so, see the asv benchmark result).
So I computed and add a flag testing if the result need to be sorted (the benchmark  seems to always have sorted key in the loc call).

**Update** The sorting function is now a separate private function (_reorder_indexer). It is called at the end of the get_locs function.

## Benchmark

Benchmark with the flag (I run asv compare with -s option):
<details>
Benchmarks that have got worse:

       before           after         ratio
     [39602e7d]       [da8b55af]
     <master>         <multiindex_sort_loc_order_issue_22797>
+      5.62±0.2μs       6.27±0.2μs     1.11  index_cached_properties.IndexCache.time_shape('Float64Index')
+      6.57±0.2μs       7.49±0.2μs     1.14  index_cached_properties.IndexCache.time_shape('TimedeltaIndex')
</details>

Benchmark without flag:
<details>
Benchmarks that have got worse:

       before           after         ratio
     [39602e7d]       [c786822a]
     <master>         <multiindex_sort_loc_order_issue_22797~1>
+     2.49±0.02ms      2.87±0.01ms     1.15  ctors.SeriesConstructors.time_series_constructor(<class 'list'>, False, 'int')
+        2.53±0ms      2.91±0.01ms     1.15  ctors.SeriesConstructors.time_series_constructor(<class 'list'>, True, 'int')
+      29.2±0.7ms      33.1±0.02ms     1.13  frame_ctor.FromLists.time_frame_from_lists
+        87.2±1ms         98.9±1ms     1.13  frame_ctor.FromRecords.time_frame_from_records_generator(None)
+     12.8±0.09ms      14.3±0.09ms     1.11  groupby.MultiColumn.time_col_select_numpy_sum
+      5.62±0.2μs       6.32±0.4μs     1.12  index_cached_properties.IndexCache.time_shape('Float64Index')
+     4.96±0.02ms      5.71±0.01ms     1.15  indexing.MultiIndexing.time_index_slice
+        2.91±0ms      3.29±0.01ms     1.13  inference.ToNumeric.time_from_numeric_str('coerce')
+        2.92±0ms      3.29±0.01ms     1.13  inference.ToNumeric.time_from_numeric_str('ignore')
+     3.45±0.01ms      3.84±0.01ms     1.11  series_methods.Map.time_map('lambda', 'object')
+      29.3±0.2ms      33.2±0.04ms     1.13  strings.Methods.time_len
</details>

## Checklist

- [x] closes #22797
- [x] tests added / passed
- [x] passes `black pandas`
- [x] passes `git diff upstream/master -u -- "*.py" | flake8 --diff`
- [x] whatsnew entry


Labels:
name: Indexing
description: Related to indexing on series/frames, not to indexes themselves
name: MultiIndex

Comments:
This is kind of what I did in another PR #29190.
There is a bit of a performance drop on some benchmarking test.

To resume the two solution I made a PR for:
- In this PR: I do not modify the way the indexer is computed, and I sorted everything at the end.
- PR #29190: I keep the indexer sorted at all time, but with a bit of a performance drop for some benchmark, and improvement on other.

When adding test cases, I come upon the following question.
What should be the ouput in this case
```
df = pd.DataFrame(
      np.arange(12).reshape((4, 3)),
      index=[["a", "a", "b", "b"], [1, 2, 1, 2]],
      columns=[["Ohio", "Ohio", "Colorado"], ["Green", "Red", "Green"]],
      )

df.loc[(slice(None), [1,2]), :]                                                                                                
# actual output
     Ohio     Colorado
    Green Red    Green
a 1     0   1        2
  2     3   4        5
b 1     6   7        8
  2     9  10       11

# maybe expected
     Ohio     Colorado
    Green Red    Green
a 2     3   4        5
b 2     9  10       11
a 1     0   1        2
b 1     6   7        8

# or
# maybe expected
     Ohio     Colorado
    Green Red    Green
a 2     3   4        5
  1     0   1        2
b 2     9  10       11
  1     6   7        8
```


this got a bit lost, can you merge master and ping on green.
> i think it might be worthile to remove need_sort entirely (from the main code) and just _always_ call _reorder_indexer where you can determine the need_sort (and just bail early if you don't need it).

This make the code more readable, thanks for the advice.

I merged master too.

I also added testing for columns selection.


@nrebena sorry, if you'd merge master again. ping on green.
@jreback Merged and green
thanks @nrebena very nice! sorry took a long time :>
@jreback It was a pleasure, I learned a lot. Thank for the guidance.