Shiny应用对接Redshift数据拉取缓慢问题求助
Great question—your observation that Redshift outperforms PostgreSQL on query execution but slows down dramatically when loading results into an R dataframe aligns perfectly with core differences between columnar and row-based databases, plus how their drivers interact with R. Let’s break down the key technical reasons:
1. Columnar vs. Row-Based Storage Layout
Redshift is a columnar database, meaning it stores data in column-wise blocks rather than entire rows. This is why your dbSendQuery runs so fast: analytical queries (like your SELECT * FROM customers) only need to scan relevant columns, which Redshift optimizes for speed.
However, R dataframes are row-based structures—each entry represents a full row of data. To populate the dataframe, Redshift has to take data stored in separate column blocks and stitch them together into rows. This "column-to-row" transformation is computationally heavy: it requires coordinating data from multiple disk locations, reordering values to match row boundaries, and assembling each row in memory.
PostgreSQL, by contrast, is a row-based database. It stores entire rows together on disk, so fetching results means reading pre-assembled rows directly into memory—this matches the dataframe’s structure almost perfectly, hence the fast fetch times.
2. JDBC Driver Overhead & Implementation Gaps
You’re using Redshift’s JDBC driver via R’s JDBC package, which relies on the rJava bridge. This adds an extra layer of abstraction compared to PostgreSQL’s native RPostgreSQL driver, which is tightly integrated with R’s data structures:
- Redshift’s JDBC driver first converts its columnar result set into the JDBC standard row-based format before R can process it. This double conversion (columnar → JDBC row set → R dataframe) adds overhead that PostgreSQL avoids entirely.
- The PostgreSQL driver is optimized for direct mapping between its row-based storage and R’s dataframe objects, with minimal serialization/deserialization overhead.
3. Client-Side Data Processing Overhead
Your test results tell a clear story: Redshift’s fetch has ~32 seconds of user time, while PostgreSQL’s is only ~1.8 seconds. This means most of the slowdown happens on your R session, not over the network.
When fetching from Redshift, R has to handle large chunks of columnar data, parse each column’s values, then map them to rows in the dataframe. This involves more memory copying and data manipulation than reading pre-assembled rows from PostgreSQL. For larger datasets, this gap widens as the volume of columnar data to stitch together grows.
4. Data Type Mapping Complexity
Redshift supports specialized data types (like SUPER for semi-structured data or GEOMETRY for spatial data) that aren’t native to PostgreSQL. Even for standard types, Redshift’s compressed columnar storage format requires extra conversion steps to map to R’s data types. PostgreSQL’s type system aligns more closely with R’s, making these conversions faster and more straightforward.
Quick Optimization Tips
To mitigate this overhead, try these approaches:
- Switch to Redshift’s ODBC driver: ODBC drivers often have more efficient data transfer mechanisms for R, eliminating the Java bridge overhead.
- Unload to S3 first: For large datasets, use Redshift’s
UNLOADcommand to export data to Amazon S3 in a row-based format (like CSV or Parquet), then read the file directly into R with packages likereadrorarrow. This skips the column-to-row transformation in the driver. - Fetch in batches: Use the
nparameter infetch()to pull data in smaller chunks, reducing memory pressure and improving processing speed for very large datasets.
内容的提问来源于stack exchange,提问作者Minh

