Deciding Whether to Enable NHibernate's Second-Level Cache: A Practical Guide
A decision guide for NHibernate's second-level cache: when to enable it, how providers compare, and how to validate cache hits with statistics.
12 Jun 2026, 03:42 UTC

Your NHibernate application repeatedly loads the same reference rows from the database on every request. Enabling the second-level cache can eliminate that round-trip, but it introduces memory pressure and consistency risks that are not appropriate for every entity. The practical takeaway: enable the second-level cache only for read-heavy, shared data with infrequent writes, and choose an in-process provider for single-node deployments or an external provider for distributed ones.
The Decision: When Second-Level Cache Pays Off
The second-level cache is a store that lives outside a single ISession. The first-level cache is bound to one session and discarded when it closes; the second-level cache survives session disposal and can be shared across sessions or application nodes. That persistence is what makes it powerful and risky.
Enable it when your workload matches these constraints:
- Read-heavy, shared data. Lookup tables, country lists, product categories, or configuration entities that many sessions read.
- Infrequent writes. Data that changes rarely, so invalidation traffic stays low.
- Memory budget available. The cache consumes RAM on the application server (in-process) or on a cache server (external).
Avoid it for volatile data, write-heavy aggregates, or domains where a stale read causes incorrect business decisions.
Comparing Supported Cache Providers
NHibernate does not mandate a specific cache implementation. You select a provider by configuring a region factory class in your settings. The choice depends on your deployment topology.
| Provider | Scope | Deployment Cost | Best Fit | Primary Trade-off |
|---|---|---|---|---|
| Built-in Hashtable | In-process | None (bundled) | Single-node apps, development | Limited capacity; lost on restart; not shared across nodes |
| SysCache2 | In-process (ASP.NET) | Low | ASP.NET apps needing memory limits | Depends on ASP.NET cache; not distributed |
| Redis provider | External, distributed | Medium–High | Web farms, multi-node services | Network latency; extra infrastructure to run and monitor |
| Memcached provider | External, distributed | Medium–High | Large read clusters | Volatile storage; operational overhead |
Ehcache is sometimes mentioned in NHibernate discussions, but it is primarily a Java ecosystem tool. For .NET deployments, the practical choice is usually the built-in provider for single-node scenarios or Redis/Memcached for distributed scenarios.
Configuring NHibernate for Second-Level Cache
Configuration happens in two places: the NHibernate settings file and the entity mapping. The following examples target NHibernate 5.x and use the built-in provider, which requires no external service.
<!-- hibernate.cfg.xml or app.config -->
<property name="cache.use_second_level_cache">true</property>
<property name="cache.provider_class">NHibernate.Cache.HashtableCacheProvider</property>
<property name="generate_statistics">true</property>
Then mark cacheable entities. Use read-write for data that changes occasionally, nonstrict-read-write for rare concurrent updates, or read-only for immutable rows.
<class name="Country" table="Countries">
<cache usage="read-write"/>
<id name="Id"><generator class="native"/></id>
<property name="Name"/>
</class>
If you choose an external provider such as Redis, replace provider_class with the region factory supplied by that provider's assembly, ensure the assembly is referenced in your project, and confirm the cache service is reachable from the application host. External providers typically require connection strings or host names in configuration.
Validating Cache Behavior
NHibernate exposes runtime statistics that make validation straightforward. With generate_statistics enabled, you can inspect CacheHits and CacheMisses after running a query.
Run this test in your application or an integration test suite. It requires no special permissions beyond database read access.
var stats = sessionFactory.Statistics;
stats.Clear();
// First query: expected to miss and populate the cache
using (var session = sessionFactory.OpenSession())
{
session.Get<Country>(1);
}
// Second query: expected to hit the second-level cache
using (var session = sessionFactory.OpenSession())
{
session.Get<Country>(1);
}
// Practical check: CacheHits should be > 0 and CacheMisses should be 0
// for the second retrieval, assuming no other cache activity occurred.
if (stats.CacheHits == 0 || stats.CacheMisses > 0)
{
// Cache is not working as expected; review provider config and mappings.
}
You can also watch NHibernate's log output or a tool like NHibernate Profiler to confirm that the second query does not emit SQL against the database.
Limitations and Operational Checks
The second-level cache is not a transparent optimization. Three limitations matter most:
- Stale data risk. If invalidation is misconfigured, or if an external provider evicts entries without notifying NHibernate, sessions can read outdated values. Keep cacheable entities tightly scoped.
- Memory pressure. The built-in provider stores objects in the application process. Large object graphs or high-cardinality entities can cause GC pressure or out-of-memory errors. External providers move that pressure to a cache server but add network hops.
- Deployment complexity. External providers require running, securing, and monitoring an additional service. If that service is unreachable, behavior depends on the provider's failure mode.
To check the result in production, monitor the ratio of CacheHits to CacheMisses over time. A healthy read-heavy cache shows a high hit ratio and stable database load. If misses climb, investigate expiration settings, entity mapping coverage, or cache size limits.
If you need to revert, set cache.use_second_level_cache to false and remove <cache> elements from mappings, then restart the application. Existing cached data becomes irrelevant once the setting is disabled.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.