Search code examples
nhibernatenhibernate-caches

NHibernate SecondLevel Cache Performance


While researching the possibilities that the second level cache in nhibernate provides I tested some implementations. Currently the outcome is quite unexpected and I'am questioning if my expectations are wrong.

  1. Scenario (Read-Heavy)

At first 4 * 20000 simple objects with one string property are inserted into the database then four threads are getting them by the object-id (session.Get<SimpleObj>(id)). Each thread only accesses the ids it did create. The access-pattern is random and gets 1000 objects and then recreates the session.

while (true)
{
    using (var session = sf.OpenSession())
    using (var tx = session.BeginTransaction())
    {
        for (int i3 = 0; i3 < 1000; i3++)
        {
            long id = (long)ids[rnd.Next(19999)];
            var t = session.Get<SimpleObject>(id);

            var no = t.StringProperty;
        }

        Interlocked.Add(ref ops, 1000);

        tx.Commit();
    }
}

Results

  1. Redis 5000 reads per second
  2. MemCached 8000 reads per second (EnyimMemcached)
  3. No Caching 15000 reads per second (Same machine, TCP-IP)
  4. No Caching 25000 reads per second (Same machine, shared memory)
  5. SysCache2 200000 reads per second
  6. HashtableCacheProvider 380000 reads per second

Versions used

Is the different performance between those providers and the SysCache2 implementation to be expected?

Update 1

As Frederic pointed out the testing scenario does not make too much sense as two different classes of caching architectures with two different uses cases are compared. The first class (5 & 6) can not be scaled horizontally while 1 and 2 can. Currently the SQL-Server is running on the same machine so shared-memory as an IPC-Mechanism is used, therefore i disabled all IPC Connection possibilities an configured it to use TCP/IP to connect to the database. As result the performance of the no-cache-scenario drops about 10000 op/s which brings the fastest distributed caching provider into a reasonable distance.

With the comparison of the caching providers I want to test if these providers can be used in a session-per-request setup to cache reference data like countries, currencies or balance-sheet structures. As the performance of the best performing distributed cache is still only half the op/s of the plain NHibernate version I am not sure if this is the way to go.


Solution

  • This is no surprise to have SysCache(2) (or RtMemoryCache if you try this other one too) to perform highly better than Redis or MemCached. The formers are in-process memory caches, their use does not incur any network IO nor any inter-process communication. The laters are distributed caches, they implies at-least inter-process communications and in most cases network IO, which incurs quite higher use cost.

    There is not really any point comparing distributed caches performances to non-distributed caches performances. You should use distributed caches only if you need sharing your cache among many processes. You would not be able to use non-distributed caches if you have that requirement.

    What is surprising is to have the "no cache" scenario (so, SQL queries) to perform highly better than distributed caches.

    One key point is missing in your test description in my opinion: what drives the performance cost of SQL queries in your scenario? If the db server is hosted on the same physical host than the web server, this cost is fairly low compared to what it is in more usual setups. Some db solution like SQL server may even skip any network IO and go for inter-process in memory communications (through named pipes) when they are hosted on the same host than the web server.

    Side note: if you do not consider configuring SysCache2 for getting invalidated through SQL Server data change notifications, you should test SysCache instead, which is lighter. (This will probably not really change results in your test scenario though.)

    Addressing Update 1:

    The very first point is to be sure you actually need your cache to be distributed. Each process can safely have its own cache if:

    1. The cached data is read-only (or eventually, if having some processes serving stale cached data does not cause trouble in your application case).
    2. This does not cause to much memory consumption. (Cached data is not heavy, duplicating it among many processes would not be an issue.)

    Of course, data cache warm-up will be slower with non distributed caches, but if distributed cache performs too poorly, this is not a point.

    If you need it to be distributed, you should then evaluate if your SQL Server is a bottleneck under load or not. If under load, your SQL Server can be a bottleneck to your application, then distributed cache will help offloading it. Otherwise, better not use them if, in your setup, distributed caches are consistently performing worse than your db server.

    Side note: If only the condition (2.) is met, you may also give a try to non-distributed cache with invalidation from SQL Server notification, allowing you to avoid stale data. See here for how to configure SysCache2 for being notified by SQL Server.