@Override
  public synchronized String splitByKey(
      String key, ByteBuffer recBuf, ByteBuffer dstBuffer, boolean inclineSplit)
      throws MetadataException {

    if (this.buffer.capacity() != dstBuffer.capacity()) {
      throw new MetadataException("Segments only splits with same capacity.");
    }

    if (recordNum == 0) {
      throw new MetadataException("Segment can not be split with no records.");
    }

    if (key == null && recordNum == 1) {
      throw new MetadataException("Segment can not be split with only one record.");
    }

    // notice that key can be null here, and a null key means even split
    boolean monotonic =
        penuKey != null
            && key != null
            && lastKey != null
            && inclineSplit
            && (key.compareTo(lastKey)) * (lastKey.compareTo(penuKey)) > 0;

    int n = recordNum;

    // actual index of key just smaller than the insert, -2 for null key
    int pos = key != null ? binaryInsertOnKeys(key) - 1 : -2;

    int sp; // virtual index to split
    if (monotonic) {
      // new entry into part with more space
      sp = key.compareTo(lastKey) > 0 ? Math.max(pos + 1, n / 2) : Math.min(pos + 2, n / 2);
    } else {
      sp = n / 2;
    }

    // little different from InternalSegment, only the front edge key can not split
    sp = sp <= 0 ? 1 : sp;

    short recordLeft = this.recordNum;
    // prepare header for dstBuffer
    short length = this.length,
        freeAddr = (short) dstBuffer.capacity(),
        recordNum = 0,
        pairLength = 0;
    long prevSegAddress = this.prevSegAddress, nextSegAddress = this.nextSegAddress;

    int recSize, keySize;
    String mKey, sKey = null;
    ByteBuffer srcBuf;
    int aix; // aix for actual index on keyAddressList
    n = key == null ? n - 1 : n; // null key
    // TODO: implement bulk split further
    for (int ix = sp; ix <= n; ix++) {
      if (ix == pos + 1) {
        // migrate newly insert
        srcBuf = recBuf;
        recSize = recBuf.capacity();
        mKey = key;
        keySize = 4 + mKey.getBytes().length;

        recBuf.clear();
      } else {
        srcBuf = this.buffer;
        // pos equals -2 if key is null
        aix = (ix > pos) && (pos != -2) ? ix - 1 : ix;
        mKey = getKeyByIndex(aix);
        keySize = 4 + mKey.getBytes().length;

        // prepare on this.buffer
        this.buffer.clear();
        this.buffer.position(getOffsetByIndex(aix) + keySize);
        recSize = getRecordLength();
        this.buffer.limit(this.buffer.position() + recSize);

        recordLeft--;
      }

      if (ix == sp) {
        // search key is the first key in split segment
        sKey = mKey;
      }

      freeAddr -= recSize + keySize;
      dstBuffer.position(freeAddr);
      ReadWriteIOUtils.write(mKey, dstBuffer);
      dstBuffer.put(srcBuf);

      dstBuffer.position(SchemaFileConfig.SEG_HEADER_SIZE + pairLength);
      ReadWriteIOUtils.write(freeAddr, dstBuffer);

      recordNum++;
      pairLength += 2;
    }

    // compact and update status
    this.recordNum = recordLeft;
    compactRecords();
    if (sp > pos + 1 && key != null) {
      // new insert shall be in this
      if (insertRecord(key, recBuf) < 0) {
        throw new ColossalRecordException(key, recBuf.capacity());
      }
    }

    // flush dstBuffer header
    dstBuffer.clear();
    ReadWriteIOUtils.write(length, dstBuffer);
    ReadWriteIOUtils.write(freeAddr, dstBuffer);
    ReadWriteIOUtils.write(recordNum, dstBuffer);
    ReadWriteIOUtils.write(pairLength, dstBuffer);
    ReadWriteIOUtils.write(prevSegAddress, dstBuffer);
    ReadWriteIOUtils.write(nextSegAddress, dstBuffer);
    // FIXME flag of split page is not always the same
    ReadWriteIOUtils.write(getFlag(), dstBuffer);

    penuKey = null;
    lastKey = null;
    return sKey;
  }
<b>Segment Structure:</b>
<li>(25 byte: header)
<li>1 short: length, segment length
<li>1 short: freeAddr, start offset of records
<li>1 short: recordNum, amount of records in this segment
<li>1 short: pairLength, length of key-address in bytes
<li>1 long (8 bytes): prevSegIndex, previous segment index
<li>1 long (8 bytes): nextSegIndex, next segment index
<li>1 bit: delFlag, delete flag
<li>1 bit: aliasFlag, whether alias existed<br>
    (--- checksum, parent record address, max/min record key may be contained further ---)
<li><s>var length: key-address pairs, begin at 25 bytes offset, length of pairLength </s><br>
<li>var length: ORDERED record offset<br>
    ... empty space ...
<li>var length: (record key, record body) * recordNum
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/mtree/impl/pbtree/schemafile/WrappedSegment.java