SQL Server 2025 Series : This New Locking Feature Changes Everything – Full Demo on Optimized Locking!

If you work with high-concurrency OLTP workloads, SQL Server 2025 Optimized Locking is one of the most practical improvements to understand. In this demo, we use two databases: one with optimized locking disabled and one with it enabled. Both databases are configured with Accelerated Database Recovery (ADR) and Read Committed Snapshot Isolation (RCSI), while only one database has OPTIMIZED_LOCKING = ON.

Why this feature matters

The real value of optimized locking is simple: it helps reduce lock footprint during write activity, which can lower blocking in busy systems. The attached demo is designed exactly for that comparison by creating two identical databases—Billing_OFF and Billing_ON—and toggling only the optimized locking setting between them.

Demo setup

Start by creating the two demo databases and enabling the required database options:

USE master;
GO
DROP DATABASE IF EXISTS Billing_OFF;
DROP DATABASE IF EXISTS Billing_ON;
GO
CREATE DATABASE Billing_OFF;
CREATE DATABASE Billing_ON;
GO
-- Both databases need ADR enabled
ALTER DATABASE Billing_OFF SET ACCELERATED_DATABASE_RECOVERY = ON;
ALTER DATABASE Billing_ON SET ACCELERATED_DATABASE_RECOVERY = ON;
GO
-- RCSI is needed for lock-after-qualification (LAQ) demo
ALTER DATABASE Billing_OFF SET READ_COMMITTED_SNAPSHOT ON WITH ROLLBACK IMMEDIATE;
ALTER DATABASE Billing_ON SET READ_COMMITTED_SNAPSHOT ON WITH ROLLBACK IMMEDIATE;
GO
-- Only one database gets optimized locking
ALTER DATABASE Billing_OFF SET OPTIMIZED_LOCKING = OFF;
ALTER DATABASE Billing_ON SET OPTIMIZED_LOCKING = ON;
GO
-- Verify settings
SELECT
name,
is_accelerated_database_recovery_on,
is_read_committed_snapshot_on,
is_optimized_locking_on
FROM sys.databases
WHERE name IN ('Billing_OFF', 'Billing_ON');
GO

Build the test table

Next, create the same table in both databases and open a transaction so you can inspect locks while the transaction is still active.

Setup for optimized locking OFF

USE Billing_OFF;
GO
DROP TABLE IF EXISTS dbo.InvoiceLedger;
GO
CREATE TABLE dbo.InvoiceLedger
(
InvoiceId int NOT NULL,
AmountDue decimal(10,2) NULL
);
GO
INSERT INTO dbo.InvoiceLedger (InvoiceId, AmountDue)
VALUES (1001, 1200.00),
(1002, 850.00),
(1003, 430.00);
GO
BEGIN TRAN;
UPDATE dbo.InvoiceLedger
SET AmountDue = AmountDue + 50.00;
SELECT
request_session_id,
resource_type,
request_mode,
resource_description
FROM sys.dm_tran_locks
WHERE request_session_id = @@SPID
AND resource_type IN ('PAGE', 'RID', 'KEY', 'XACT')
ORDER BY resource_type, request_mode;
-- Keep transaction open for observation
WAITFOR DELAY '00:00:20';
COMMIT TRAN;
GO

This script updates all rows and keeps the transaction open for 20 seconds so you can inspect the acquired locks in the Billing_OFF database.

Setup for optimized locking ON

USE Billing_ON;
GO
DROP TABLE IF EXISTS dbo.InvoiceLedger;
GO
CREATE TABLE dbo.InvoiceLedger
(
InvoiceId int NOT NULL,
AmountDue decimal(10,2) NULL
);
INSERT INTO dbo.InvoiceLedger (InvoiceId, AmountDue)
VALUES (1001, 1200.00),
(1002, 850.00),
(1003, 430.00);
GO
BEGIN TRAN;
UPDATE dbo.InvoiceLedger
SET AmountDue = AmountDue + 50.00;
SELECT
request_session_id,
resource_type,
request_mode,
resource_description
FROM sys.dm_tran_locks
WHERE request_session_id = @@SPID
AND resource_type IN ('PAGE', 'RID', 'KEY', 'XACT')
ORDER BY resource_type, request_mode;
WAITFOR DELAY '00:00:20';
COMMIT TRAN;
GO

This is the matching script for Billing_ON, allowing you to compare lock behavior when optimized locking is enabled.

Concurrency test

To demonstrate blocking behavior, open two sessions against each database.

Session 1 – hold an update open

Optimized locking OFF

USE Billing_OFF;
GO
BEGIN TRAN;
UPDATE dbo.InvoiceLedger
SET AmountDue = AmountDue + 25.00
WHERE InvoiceId = 1001;
WAITFOR DELAY '00:00:20';
COMMIT TRAN;
GO

This session updates InvoiceId = 1001 and intentionally holds the transaction for 20 seconds.

Optimized locking ON

USE Billing_ON;
GO
BEGIN TRAN;
UPDATE dbo.InvoiceLedger
SET AmountDue = AmountDue + 25.00
WHERE InvoiceId = 1001;
WAITFOR DELAY '00:00:20';
COMMIT TRAN;
GO

This is the same workload pattern, but executed in the database where optimized locking is enabled.

Session 2 – concurrent update

Optimized locking OFF

USE Billing_OFF;
GO
BEGIN TRAN;
UPDATE dbo.InvoiceLedger
SET AmountDue = AmountDue + 25.00
WHERE InvoiceId = 1002;
COMMIT TRAN;
GO

Run this in a second session while Session 1 is still open.

Optimized locking ON

USE Billing_ON;
GO
BEGIN TRAN;
UPDATE dbo.InvoiceLedger
SET AmountDue = AmountDue + 25.00
WHERE InvoiceId = 1002;
COMMIT TRAN;
GO

Again, this is the same concurrent update, but without optimized locking.

Monitoring script

Use the following monitoring query to observe waits, request state, and lock information for both sessions while the demo is running:

-- Replace the session IDs below with the two session IDs used in your demo windows.
SELECT
r.session_id,
r.status,
r.command,
r.wait_type,
r.wait_time,
r.wait_resource,
t.text
FROM sys.dm_exec_requests r
CROSS APPLY sys.dm_exec_sql_text(r.sql_handle) t
WHERE r.session_id IN (62, 171);
GO
SELECT
request_session_id,
resource_type,
request_mode,
request_status,
resource_description
FROM sys.dm_tran_locks
WHERE request_session_id IN (62, 171)
ORDER BY request_session_id, resource_type, request_mode;
GO

Expected takeaway

The demo is structured to show that the same update workload behaves differently depending on whether OPTIMIZED_LOCKING is OFF or ON. Because both environments are identically configured except for the optimized locking setting, any change in observed lock behavior is attributable to that feature.

Final thoughts

SQL Server 2025 Optimized Locking is not just a checkbox feature—it directly changes how you demonstrate concurrency, lock management, and blocking reduction to customers. If you want a clean live demo, the attached billing scripts are perfect because they isolate the feature clearly and make the before-vs-after comparison easy to explain.


Watch the Full Demo

I’ve recorded a complete walkthrough of this setup on my YouTube channel JBSWiki. If you’re a visual learner, go check it out!

👉 Watch here: https://www.youtube.com/watch?v=-XQj5YtnuEY


Thank You,
Vivek Janakiraman

Disclaimer:
The views expressed on this blog are mine alone and do not reflect the views of my company or anyone else. All postings on this blog are provided “AS IS” with no warranties, and confers no rights.

SQL Server Unused Indexes: Identification, Monitoring, and Management

Indexes are crucial for optimizing query performance in SQL Server. However, not all indexes are used effectively; some might remain unused, consuming space and resources unnecessarily. In this comprehensive blog, we’ll delve into the concept of unused indexes, how to identify them, the potential risks of deleting them, and best practices for managing them. We’ll also explore real-world scenarios and provide the necessary T-SQL scripts for monitoring and handling unused indexes.


🔍 What is an Unused Index?

An unused index is an index that exists in the database but is not used by the SQL Server query optimizer. This could be due to several reasons:

  1. Outdated Query Patterns: The index may have been useful for queries that are no longer executed.
  2. Changes in Data Distribution: Alterations in data patterns may render the index less effective or redundant.
  3. Incorrect Index Design: The index might not align with the current workload or data structure.

Unused indexes can lead to unnecessary resource consumption, such as additional storage space and increased overhead during data modification operations (INSERT, UPDATE, DELETE).

Risks of Removing Unused Indexes ⚠️

While removing unused indexes can free up resources, it can also lead to unexpected performance issues if not done carefully. Here are some potential risks:

  1. Impact on Rarely Used Queries: An index might appear unused but could be critical for infrequent queries, such as quarterly reports.
  2. Incorrect Monitoring Period: A short monitoring period might not capture all usage patterns, leading to incorrect conclusions.

Best Practices for Monitoring Unused Indexes 📊

  1. Extended Monitoring Period: Monitor index usage over an extended period (e.g., several months) to capture all usage patterns.
  2. Analyze Workload Patterns: Understand your workload and identify critical periods (e.g., end-of-month processing).
  3. Test Before Removing: Always test the impact of removing an index in a non-production environment.

Advantages of Managing Unused Indexes 🌟

  1. Improved Performance: Reducing the number of unused indexes can improve performance for data modification operations.
  2. Reduced Storage Costs: Freeing up storage space by removing unused indexes.
  3. Simplified Maintenance: Fewer indexes to maintain and monitor.

🔧 How to Identify Unused Indexes

Identifying unused indexes involves monitoring the usage statistics provided by SQL Server. The sys.dm_db_index_usage_stats dynamic management view (DMV) is a valuable resource for this purpose.

📋 T-SQL Script to Identify Unused Indexes

The following script retrieves information about indexes that haven’t been used since the last server restart:

SELECT 
    i.name AS IndexName,
    i.object_id,
    o.name AS TableName,
    s.name AS SchemaName,
    i.index_id,
    u.user_seeks,
    u.user_scans,
    u.user_lookups,
    u.user_updates
FROM 
    sys.indexes AS i
JOIN 
    sys.objects AS o ON i.object_id = o.object_id
JOIN 
    sys.schemas AS s ON o.schema_id = s.schema_id
LEFT JOIN 
    sys.dm_db_index_usage_stats AS u 
    ON i.object_id = u.object_id AND i.index_id = u.index_id
WHERE 
    i.is_primary_key = 0
    AND i.is_unique_constraint = 0
    AND o.type = 'U'
    AND u.index_id IS NULL
    AND u.object_id IS NULL
ORDER BY 
    s.name, o.name, i.name;

This script filters out primary key and unique constraint indexes, focusing on user-created indexes that have not been used since the last server restart.


⚠️ Potential Issues with Deleting Unused Indexes

While removing unused indexes can free up resources, it also carries potential risks:

  1. Hidden Usage: Some indexes may not show usage in the DMV statistics if they are used infrequently or during specific maintenance operations.
  2. Future Requirements: An index deemed unused might be needed for future queries or batch jobs, especially if they run infrequently (e.g., quarterly reports).
  3. Inaccurate Assessment: Short monitoring periods can lead to incorrect conclusions about an index’s utility.

⏲️ Best Time Frame for Monitoring

It’s advisable to monitor index usage over a prolonged period, ideally encompassing a full business cycle (e.g., monthly, quarterly). This ensures that all potential usage patterns, including infrequent but critical operations, are accounted for.


🛠️ Handling Unused Indexes

Best Practices for Managing Unused Indexes

  1. Prolonged Monitoring: As mentioned, extend the monitoring period to capture all usage patterns.
  2. Review Before Deletion: Before removing an index, consult with application developers and database administrators to understand its purpose.
  3. Testing and Staging: Always test the impact of removing an index in a staging environment before applying changes to production.
  4. Documentation: Maintain documentation of all indexes and their intended purpose to avoid unintentional removal.

📜 Example Scenarios

1. Beneficial Removal of an Unused Index

Scenario: A retail company finds an unused index on a transactional table that has not been utilized for over a year. The index occupies significant disk space and slows down data modification operations.

Action: After thorough analysis and consultation, the company decides to remove the index, resulting in improved performance and reduced storage costs.

T-SQL for Removing the Index:

DROP INDEX IndexName ON SchemaName.TableName;

2. Problematic Removal of a Used Index

Scenario: A financial services company removes an index that appears unused based on a short monitoring period. The index was actually used for a quarterly reconciliation job, leading to significantly slower performance and extended processing times during the next quarter.

Lesson Learned: The company learned the importance of comprehensive monitoring and consultation before making changes.


🏢 Business Use Cases

Cost Optimization

Removing unused indexes can free up valuable disk space and reduce maintenance overhead, leading to cost savings. This is particularly beneficial for organizations with large databases where storage costs are a significant concern.

Performance Enhancement

By eliminating unnecessary indexes, the performance of data modification operations can be improved, leading to faster transaction processing and more efficient database operations.


🏁 Conclusion

Managing unused indexes in SQL Server requires careful analysis and a comprehensive approach. While removing unused indexes can provide benefits like reduced storage costs and improved performance, it is crucial to ensure that the indexes are genuinely unused and not required for infrequent operations. By following best practices and leveraging the right tools, you can optimize your SQL Server environment effectively.

For any questions or further guidance, feel free to reach out or leave a comment! Happy optimizing! 🚀

For more tutorials and tips on SQL Server, including performance tuning and database management, be sure to check out our JBSWiki YouTube channel.

Thank You,
Vivek Janakiraman

Disclaimer:
The views expressed on this blog are mine alone and do not reflect the views of my company or anyone else. All postings on this blog are provided “AS IS” with no warranties, and confers no rights.

Understanding Max Server Memory and Minimum Server Memory in SQL Server

SQL Server’s memory management is a crucial aspect of its performance and stability. Two important settings in this context are Max Server Memory and Minimum Server Memory. These settings help SQL Server efficiently manage its memory usage, ensuring optimal performance and avoiding system instability.

What is Max Server Memory?

Max Server Memory limits the amount of memory that SQL Server can use for its operations. This setting helps prevent SQL Server from consuming too much memory, which could negatively impact the operating system and other applications running on the same server.

Importance of Max Server Memory
  1. System Stability: By capping the memory usage, you ensure that enough memory is available for the OS and other applications, preventing system-wide slowdowns or crashes.
  2. Performance Optimization: Properly configuring Max Server Memory allows SQL Server to use memory efficiently, reducing the need for frequent data disk reads and writes, which can significantly slow down performance.
  3. Resource Allocation: In environments where SQL Server shares resources with other applications, setting an appropriate Max Server Memory ensures fair resource distribution.
Calculating and Setting Max Server Memory

To start, you should leave enough memory for the operating system and any other applications. A common approach is to allocate at least 4 GB or 10% of total system memory (whichever is larger) to the OS. The rest can be allocated to SQL Server as Max Server Memory.

Example Calculation: Suppose you have a server with 32 GB of RAM:

  1. Allocate memory for the OS and other applications:
    • 4 GB (minimum recommended) or 10% of 32 GB = 3.2 GB
    • Choosing the larger value: 4 GB
  2. Subtract this from the total RAM:
    • 32 GB – 4 GB = 28 GB
  3. Set Max Server Memory to 28 GB.

Setting Max Server Memory in SQL Server: You can set Max Server Memory using SQL Server Management Studio (SSMS) or T-SQL commands:

  • Using SSMS:
    1. Open SSMS and connect to your SQL Server instance.
    2. Right-click on the server name and select “Properties.”
    3. Navigate to the “Memory” tab.
    4. Set the “Maximum server memory (in MB)” to the calculated value.
  • Using T-SQL:
EXEC sp_configure 'show advanced options', 1;
RECONFIGURE;
EXEC sp_configure 'max server memory', 28672; -- Set to 28 GB (28 * 1024 MB)
RECONFIGURE;

What is Minimum Server Memory?

Minimum Server Memory specifies the minimum amount of memory SQL Server should attempt to reserve after it has started. However, it’s worth noting that SQL Server doesn’t start with this memory allocation; instead, it gradually grows its memory usage up to this amount as needed.

Importance of Minimum Server Memory
  1. Ensuring Performance: Setting a minimum ensures that SQL Server has enough memory for its operations, which is crucial for maintaining performance under varying workloads.
  2. Avoiding Memory Pressure: It helps avoid situations where SQL Server might have to give up memory under pressure, which could degrade performance.

Potential Issues with Incorrect Settings

  1. Setting Max Server Memory Too High: This can lead to insufficient memory for the OS and other applications, causing system instability, swapping, and even crashes.
  2. Setting Max Server Memory Too Low: SQL Server might not have enough memory for optimal performance, leading to excessive disk I/O, slower queries, and reduced throughput.
  3. Incorrect Minimum Server Memory: If set too high, it can reserve more memory than necessary, potentially starving other processes. If set too low, SQL Server might not have enough resources to function efficiently under load.

Best Practices

  1. Monitor and Adjust: Regularly monitor memory usage and adjust settings based on the workload and system performance.
  2. Consider the Entire System: Take into account the memory requirements of the OS and other applications on the server.
  3. Start Conservative: Begin with a conservative estimate and gradually increase Max Server Memory as needed, observing the system’s behavior.

In conclusion, correctly configuring Max Server Memory and Minimum Server Memory is vital for SQL Server’s performance and the overall system’s stability. By carefully calculating and setting these values, you can ensure a balanced and efficient use of resources, providing a stable and high-performing environment for your SQL Server workloads.

For more tutorials and tips on SQL Server, including performance tuning and database management, be sure to check out our JBSWiki YouTube channel.

Thank You,
Vivek Janakiraman

Disclaimer:
The views expressed on this blog are mine alone and do not reflect the views of my company or anyone else. All postings on this blog are provided “AS IS” with no warranties, and confers no rights.