Skip to content

[Bug] Low throughput of PostgreSQL CDC streaming job, single-core CPU bottleneck on BE write path #67123

Description

@gaogao110

Search before asking

  • I had searched in the issues and found no similar issues.

Version

4.1.3

What's Wrong?

When synchronizing PostgreSQL incremental data via Doris Streaming Job, the import throughput is extremely low and cannot reach business expectations.
Server overall resources are abundant: the machine has 60.7% idle CPU and plenty of free memory, no swap pressure, and IO wait is low. However, the doris_be process consistently occupies 100% single-core CPU, which becomes the strict bottleneck of CDC import.
The upstream Flink task CPU load is only 6.3%, which proves that the data source side is not the bottleneck. The performance bottleneck is completely inside the Doris BE write logic.

Top System Status
%Cpu(s): 24.3 us, 4.7 sy, 0.0 ni, 60.7 id, 6.9 wa, 0.9 hi, 2.5 si, 0.0 st
MiB Mem : 31301.9 total, 12756.9 free, 16703.9 used, 2284.1 buff/cache
MiB Swap: 0.0 total, 0.0 free, 0.0 used. 14598.0 avail Mem

PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND

3923736 root 20 0 31.1g 1.5g 256252 S 102.0 5.0 51:40.35 doris_be
3928325 root 20 0 16.5g 7.6g 19520 S 6.3 24.8 13:12.08 java

  • BE single-core full load, multi-core idle, typical single-thread serialization bottleneck
  • Upstream Flink Java task almost no pressure
  • Memory is sufficient, no IO bottleneck
Image

Perf Flame Graph Analysis
CPU hotspot is concentrated in the single-thread write path of FragmentMgrAsyn asynchronous thread pool:
FragmentMgrAsyn
└── doris::AsyncResultWriter::process_block
└── doris::VTabletWriter::write
└── doris::VRowDistribution::generate_rows_distribution
└── doris::VRowDistribution::__generate_rows_distribution_for_non_auto_partition
├── doris::OlapTableFinder::find_tables
└── doris::Block::dump_data
└── doris::ColumnWithTypeAndName::to_string
└── doris::IDataType::to_string
Two major CPU overheads:

  1. Non-auto partition routing: For standard Range partition tables (not AUTO-RANGE), every row needs full tablet metadata lookup via find_tables, which causes heavy per-row computation.
  2. Block serialization overhead: Frequent dump_data / to_string conversion consumes massive user CPU during CDC stream load writing.
Image

What You Expected?

fix it

How to Reproduce?

To Reproduce

  1. Create a standard Range partitioned table (non AUTO-RANGE)
  2. Create PostgreSQL CDC streaming job to consume incremental data
  3. Generate continuous PG incremental traffic
  4. Observe low import throughput and 100% single-core BE CPU usage

Anything Else?

No response

Are you willing to submit PR?

  • Yes I am willing to submit a PR!

Code of Conduct

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions