-
Notifications
You must be signed in to change notification settings - Fork 66
Support non-uniform batch size #114
Changes from all commits
451015d
9d121ba
5e68de6
38f82b7
7e517bd
cd82c4c
62d8ee3
cd63d6d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
package mydump | ||
|
||
import ( | ||
"math" | ||
"os" | ||
|
||
"github.com/pkg/errors" | ||
|
@@ -46,13 +47,89 @@ func (rs regionSlice) Less(i, j int) bool { | |
|
||
//////////////////////////////////////////////////////////////// | ||
|
||
func MakeTableRegions(meta *MDTableMeta, columns int, batchSize int64) ([]*TableRegion, error) { | ||
func AllocateEngineIDs( | ||
filesRegions []*TableRegion, | ||
dataFileSizes []float64, | ||
batchSize float64, | ||
batchImportRatio float64, | ||
tableConcurrency float64, | ||
) { | ||
totalDataFileSize := 0.0 | ||
for _, dataFileSize := range dataFileSizes { | ||
totalDataFileSize += dataFileSize | ||
} | ||
|
||
// No need to batch if the size is too small :) | ||
if totalDataFileSize <= batchSize { | ||
return | ||
} | ||
|
||
curEngineID := 0 | ||
curEngineSize := 0.0 | ||
curBatchSize := batchSize | ||
|
||
// import() step will not be concurrent. | ||
// If multiple Batch end times are close, it will result in multiple | ||
// Batch import serials. We need use a non-uniform batch size to create a pipeline effect. | ||
// Here we calculate the total number of engines, which is needed to compute the scale up | ||
// | ||
// Total/B1 = 1/(1-R) * (N - 1/beta(N, R)) | ||
// ≲ N/(1-R) | ||
// | ||
// We use a simple brute force search since the search space is extremely small. | ||
ratio := totalDataFileSize * (1 - batchImportRatio) / batchSize | ||
n := math.Ceil(ratio) | ||
logGammaNPlusR, _ := math.Lgamma(n + batchImportRatio) | ||
logGammaN, _ := math.Lgamma(n) | ||
logGammaR, _ := math.Lgamma(batchImportRatio) | ||
invBetaNR := math.Exp(logGammaNPlusR - logGammaN - logGammaR) // 1/B(N, R) = Γ(N+R)/Γ(N)Γ(R) | ||
for { | ||
if n <= 0 || n > tableConcurrency { | ||
n = tableConcurrency | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should we check whether user set a unreasonable table conc? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. in addition, it may be better to compute There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can't directly set Example of too large: 8T table with table-conc = 8, forcing each batch to be ~1T, causing pressure on importer. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I means we compute |
||
break | ||
} | ||
realRatio := n - invBetaNR | ||
if realRatio >= ratio { | ||
// we don't have enough engines. reduce the batch size to keep the pipeline smooth. | ||
curBatchSize = totalDataFileSize * (1 - batchImportRatio) / realRatio | ||
break | ||
} | ||
invBetaNR *= 1 + batchImportRatio/n // Γ(X+1) = X * Γ(X) | ||
n += 1.0 | ||
} | ||
|
||
for i, dataFileSize := range dataFileSizes { | ||
filesRegions[i].EngineID = curEngineID | ||
curEngineSize += dataFileSize | ||
|
||
if curEngineSize >= curBatchSize { | ||
curEngineSize = 0 | ||
curEngineID++ | ||
|
||
i := float64(curEngineID) | ||
// calculate the non-uniform batch size | ||
if i >= n { | ||
curBatchSize = batchSize | ||
} else { | ||
// B_(i+1) = B_i * (I/W/(N-i) + 1) | ||
curBatchSize *= batchImportRatio/(n-i) + 1.0 | ||
} | ||
} | ||
} | ||
} | ||
|
||
func MakeTableRegions( | ||
meta *MDTableMeta, | ||
columns int, | ||
batchSize int64, | ||
batchImportRatio float64, | ||
tableConcurrency int, | ||
) ([]*TableRegion, error) { | ||
// Split files into regions | ||
filesRegions := make(regionSlice, 0, len(meta.DataFiles)) | ||
dataFileSizes := make([]float64, 0, len(meta.DataFiles)) | ||
|
||
prevRowIDMax := int64(0) | ||
curEngineID := 0 | ||
curEngineSize := int64(0) | ||
for _, dataFile := range meta.DataFiles { | ||
dataFileInfo, err := os.Stat(dataFile) | ||
if err != nil { | ||
|
@@ -61,10 +138,9 @@ func MakeTableRegions(meta *MDTableMeta, columns int, batchSize int64) ([]*Table | |
dataFileSize := dataFileInfo.Size() | ||
rowIDMax := prevRowIDMax + dataFileSize/(int64(columns)+2) | ||
filesRegions = append(filesRegions, &TableRegion{ | ||
EngineID: curEngineID, | ||
DB: meta.DB, | ||
Table: meta.Name, | ||
File: dataFile, | ||
DB: meta.DB, | ||
Table: meta.Name, | ||
File: dataFile, | ||
Chunk: Chunk{ | ||
Offset: 0, | ||
EndOffset: dataFileSize, | ||
|
@@ -73,13 +149,9 @@ func MakeTableRegions(meta *MDTableMeta, columns int, batchSize int64) ([]*Table | |
}, | ||
}) | ||
prevRowIDMax = rowIDMax | ||
|
||
curEngineSize += dataFileSize | ||
if curEngineSize > batchSize { | ||
curEngineSize = 0 | ||
curEngineID++ | ||
} | ||
dataFileSizes = append(dataFileSizes, float64(dataFileSize)) | ||
} | ||
|
||
AllocateEngineIDs(filesRegions, dataFileSizes, float64(batchSize), batchImportRatio, float64(tableConcurrency)) | ||
return filesRegions, nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -56,7 +56,18 @@ addr = "127.0.0.1:8808" | |
read-block-size = 65536 # Byte (default = 64 KB) | ||
# minimum size (in terms of source data file) of each batch of import. | ||
# Lightning will split a large table into multiple engine files according to this size. | ||
batch-size = 10_737_418_240 # Byte (default = 10 GB) | ||
batch-size = 107_374_182_400 # Byte (default = 100 GiB) | ||
|
||
# Engine file needs to be imported sequentially. Due to table-concurrency, multiple engines will be | ||
# imported nearly the same time, and this will create a queue and this wastes resources. Therefore, | ||
# Lightning will slightly increase the size of the first few batches to properly distribute | ||
# resources. The scale up is controlled by this parameter, which expresses the ratio of duration | ||
# between the "import" and "write" steps with full concurrency. This can be calculated as the ratio | ||
# (import duration / write duration) of a single table of size around 1 GB. The exact timing can be | ||
# found in the log. If "import" is faster, the batch size anomaly is smaller, and a ratio of | ||
# zero means uniform batch size. This value should be in the range (0 <= batch-import-ratio < 1). | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. how to get There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've expanded the comment a bit. This can be calculated by I do suspect that the ratio is not a constant. It could be affected by the table structure, for instance. But for the 3 tables we've tested the ratio does approach this value. We could optimize the calculation later. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just mean give There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will we give users a series of different recommended values for different deployments later? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can explain how to choose the best value in the docs and to OPS. But we don't expect users are going to change these values unless they want to do some heavy optimization. |
||
batch-import-ratio = 0.75 | ||
|
||
# mydumper local source data directory | ||
data-source-dir = "/tmp/export-20180328-200751" | ||
# if no-schema is set true, lightning will get schema information from tidb-server directly without creating them. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not compute
N
at here directly, just try to reduce batch size?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because there's no simple formula to solve
N
inX = N - 1/beta(N, R)
😅.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
N
is in a limit values range, means maybe we can use a heuristic way