Blog Post

Azure Database Support Blog
2 MIN READ

Lesson Learned #427:BUFFER IO in Azure SQL Database (Query Data Store): An Expert's Guide

Jose_Manuel_Jurado's avatar
Sep 12, 2023

In Azure SQL Database, performance tuning often requires a deep dive into "wait stats" using tools like the Query Data Store (QDS). One prominent wait stat that might surface is BUFFER IO. Notably, within the context of Azure SQL Database, this wait stat corresponds to the traditional PAGEIOLATCH wait types seen in SQL Server

 

Understanding BUFFER IO

BUFFER IO signifies the waiting times Azure SQL Database undergoes when reading or writing data pages from or to the disk. High BUFFER IO waits can indicate issues with the underlying storage system or excessive read/write operations surpassing the present resource limits.

 

Root Causes:

  1. Suboptimal Storage: The efficiency of the underlying storage might not be meeting current requirements. 
  2. Inefficient Query Design: Overly broad queries can strain the system, leading to more IO.
  3. Inadequate Indexing: Missing or inefficient indexes can cause the database engine to perform excessive IO operations.
  4. No accurated statistics.

 

Fetching BUFFER IO Data using QDS

To gather insights related to BUFFER IO from the Query Data Store, use the following query:

 

 

WITH AggregatedWaitStats AS (
    SELECT
        q.query_id,
        p.plan_id,
        ws.wait_category_desc,
        SUM(ws.total_query_wait_time_ms) AS total_wait_time_ms,
        AVG(rs.avg_duration) AS avg_wait_time_ms_per_execution
    FROM sys.query_store_query q
    JOIN sys.query_store_plan p ON q.query_id = p.query_id
    JOIN sys.query_store_wait_stats ws ON p.plan_id = ws.plan_id
    JOIN sys.query_store_runtime_stats rs ON p.plan_id = rs.plan_id
    WHERE ws.wait_category_desc = 'BUFFER IO'
    GROUP BY q.query_id, p.plan_id, ws.wait_category_desc
)
SELECT
    a.query_id,
    a.plan_id,
    a.wait_category_desc,
    a.total_wait_time_ms,
    a.avg_wait_time_ms_per_execution,
    qt.query_sql_text
FROM AggregatedWaitStats a
join sys.query_store_query Qy ON a.query_id = qy.query_id
JOIN sys.query_store_query_text qt ON qy.query_text_id = qt.query_text_id
ORDER BY a.total_wait_time_ms DESC;

 

 

Reproducing the BUFFER IO Issue

For users keen on experiencing the BUFFER IO issue in a hands-on manner, here's a demonstration:

 

 

-- 1. Create a sample table to simulate substantial data operations.
CREATE TABLE BufferIOTest (
    ID INT IDENTITY(1,1) PRIMARY KEY,
    TestData CHAR(8000)
);

-- 2. Populate the table with a large volume of data.
DECLARE @i INT = 0;
WHILE @i < 1000000
BEGIN
    INSERT INTO BufferIOTest (TestData) VALUES (REPLICATE('A', 8000));
    SET @i = @i + 1;
END;

-- 3. Clear the buffer cache to force Azure SQL to read from disk (This requires high privileges and should be executed with caution!)
-- DBCC DROPCLEANBUFFERS;

-- 4. Execute a query that will force a significant amount of IO.
SELECT COUNT(*) FROM BufferIOTest WHERE TestData LIKE '%Z%';

 

 

Note: Use the DBCC DROPCLEANBUFFERS command with caution as it clears the buffer cache, and doing so in a production environment can significantly degrade performance.

Conclusion

BUFFER IO in Azure SQL Database's Query Data Store offers a critical insight into IO-related performance challenges. Promptly addressing these waits and understanding their origin is vital for optimizing both application and database performance. This guide provides both a conceptual understanding and a hands-on demonstration to aid in tackling this prevalent issue.

 

Enjoy!

Updated Sep 12, 2023
Version 1.0

1 Comment

  • JayDing's avatar
    JayDing
    Former Employee

    For the same query, e.g. select Id,TransactionStatusId from [dbo]. [Transaction] where IsFromPIPlatform = 1 and IsDeleted = 0 and PackageId=@PackageId

     

    Depending on the query criteria, such as the packageid in this query above, does every query result in high buffer_IO or is it just a matter of chance.