Redis vs SQL Server performance
Asked Answered
W

3

16

Application performance is one of the main reason of using cache over relational database. Because it stores data in memory in the form of key value pair, we can store frequently accessed data in cache which are not changes very frequently. Reading from cache is much faster than database. Redis is one of the best solution in distributed cache market.

I was doing a performance test between Azure Redis cache and Azure SQL Server. I have created a simple ASP.NET Core application and inside that I have read data from SQL Server database as well as Redis multiple times and compare the read time duration between them. For database reading I have used Entity Framework Core and for Redis reading I have used 'Microsoft.Extensions.Caching.StackExchangeRedis'.

Model

using System;

namespace WebApplication2.Models
{
    [Serializable]
    public class Student
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int Age { get; set; }
        public string Subject { get; set; }

        public Student()
        {
            Name = string.Empty;
            Subject = string.Empty;
        }
    }
}

Entity Framework Core data context.

using Microsoft.EntityFrameworkCore;
using WebApplication2.Models;

namespace WebApplication2.Data
{
    public class StudentContext : DbContext
    {
        public StudentContext(DbContextOptions<StudentContext> options)
            : base(options)
        {
        }

        public DbSet<Student>? Students { get; set; }
    }
}

Startup class

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews();
        
    string studentDbConnectionString = Configuration.GetConnectionString("StudentDbConnectionString");
    services.AddDbContext<StudentContext>(option => option.UseSqlServer(studentDbConnectionString));

    string redisConnectionString = Configuration.GetConnectionString("RedisConnectionString");
    services.AddStackExchangeRedisCache(options =>
    {
        options.Configuration = redisConnectionString;
    });
}

appsettings.json

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
     }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "StudentDbConnectionString": "[Azure SQL Server connection string]",
    "RedisConnectionString": "[Azure Redis cache connection string]"
  }
}

Home controller

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Distributed;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using WebApplication2.Data;
using WebApplication2.Models;

namespace WebApplication2.Controllers
{
    public class HomeController : Controller
    {
        private readonly StudentContext _studentContext;
        private readonly IDistributedCache _cache;

        public HomeController(StudentContext studentContext, IDistributedCache cache)
        {
            _studentContext = studentContext;
            _cache = cache;
        }

        public IActionResult Index()
        {
            List<Student>? students = null;
            var counter = 10000;

            var sw = Stopwatch.StartNew();
            for (var i = 0; i < counter; i++)
            {
                students = _studentContext.Students.OrderBy(student => student.Id).ToList();
            }
            sw.Stop();
            ViewData["DatabaseDuraion"] = $"Database: {sw.ElapsedMilliseconds}";

            if (students != null && students.Count > 0)
            {
                List<Student> studentsFromCache;
                var key = "Students";
                _cache.Set(key, ObjectToByteArray(students));

                sw.Restart();
                for (var i = 0; i < counter; i++)
                {
                    studentsFromCache = (List<Student>)ByteArrayToObject(_cache.Get(key));
                }
                sw.Stop();
                ViewData["RedisDuraion"] = $"Redis: {sw.ElapsedMilliseconds}";
            }

            return View();
        }

        private byte[] ObjectToByteArray(object obj)
        {
            var bf = new BinaryFormatter();
            using var ms = new MemoryStream();
            bf.Serialize(ms, obj);
            return ms.ToArray();
        }

        private object ByteArrayToObject(byte[] arrBytes)
        {
            using var memStream = new MemoryStream();
            var binForm = new BinaryFormatter();
            memStream.Write(arrBytes, 0, arrBytes.Length);
            memStream.Seek(0, SeekOrigin.Begin);
            object obj = binForm.Deserialize(memStream);
            return obj;
        }
    }
}

Home\Index.cshtml view

@{
    ViewData["Title"] = "Home Page";
}

<div class="text-center">        
    <p>@ViewData["DatabaseDuraion"]</p>
    <p>@ViewData["RedisDuraion"]</p>
</div>

I have found SQL Server is faster than Redis.

SQL Server vs Redis

The ASP.NET Core application is hosted in Azure App Service with the same location with Azure SQL Server and Azure Redis.

Please let me know why Redis is slower than SQL Server?

Wedurn answered 13/7, 2020 at 18:21 Comment(9)
Can you benchmark it with github.com/dotnet/BenchmarkDotNet ?Mousterian
how did you implement SQL Tables? how do you access it? Did you use In-Memory Technology? (Hekaton)Whole
You might find different results under load. Especially if you have more complex queries with joins against SQL server. Even if SQL was faster though, Redis is often used to lessen the load on SQL. Depending on your app service plan, you can start running out of steam on SQL pretty quick with lots of users. Use Redis to cushion that.Daddy
Redis is single threaded and SQL Server read is multithreaded. Is this one of the reason SQL Server is fast?Wedurn
You are probably hitting the buffer pool from SQL server.Mousterian
Like others said, you might be getting the result of SQL server from its buffer (memory). Also, it's not a proper way to benchmark redis. You'll need reading from multiple clients to saturate the database. e.g. A hundred connecting clients reading from SQL/Redis, each reading 10000 loops, and see which one finishes earlier.Wicker
I have used github.com/dotnet/BenchmarkDotNet to benchmark the database and redis for 10000 reads. Database mean: 16.48 sec and redis mean: 29.53 sec.Wedurn
I have used JMeter and connects 100 users each reading database/redis 1000 times. There is not much difference between total time it took to finish reading database vs redis (both are near about 3 mins and 30 sec), but I saw load on database cpu. Redis is reducing some load from database in that caseWedurn
Redis markets itself as database cache. However, I suspect many of the performance advantages anticipated from Redis are also available from a properly configured database (for example using built in DB caching). Also, I haven't seen any sensible comparisons that exploit DB speed up technology (e.g. Static/pre compiled SQL). The big advantage of Redis is its simplicity which I suspect is behind many deployments.Ur
W
16

I have used github.com/dotnet/BenchmarkDotNet to benchmark the Azure SQL Server database and Azure cache for Redis for 10000 reads. SQL Server database mean: 16.48 sec and Redis mean: 29.53 sec.

I have used JMeter and connects 100 users each reading SQL Server database/Redis 1000 times. There is not much difference between total time it took to finish reading SQL Server database vs Redis (both are near about 3 mins and 30 sec), but I saw load on Azure SQL Server database DTU. The DTU goes near 100% during the test.

As a conclusion, I think speed is not the only reason to use Redis cache over SQL Server database but another reason is Redis cache reduces good amount of load from the database.

Wedurn answered 17/7, 2020 at 6:37 Comment(1)
In the case of Microsoft Azure, SQL may be more cost effective than Redis. So you can offload your main SQL instance, with a separate for cache. SQL also supports Vnet integration in its basic plan, unlike Redis. Basic Azure SQL: ~5 EUR/month, Standard Redis: ~35 EUR/month.Saffren
R
3

You don't only see performance difference here BTW. For cache, Redis is also giving you cache invalidation logic, which you need to build up in SQL In memory table. So Redis all the way when it comes to cache

Rapids answered 22/2, 2022 at 19:45 Comment(0)
E
2

Think about what's happening here

In SQL Process -> TCP -> read optimised store (single table) -> Serialisation into application models

In Redis Process -> check for cache hit -> TCP -> read optimised store (single table) -> Serialisation into application models

Redis is great, but don't mistake its purpose, if you are doing a read from an indexed table on a well optimised index then SQL is going to be quick, why would Redis be any quicker? The power of distributed cache comes in when your authoritive store or your process have to do some computations to gain to result, so effectively what you are saving by caching is CPU disk / time (be it on sql or in proc).

If you want to really increase speed it's in memory cache that you want, this however isn't as simple as it first sounds, the real trick here is a way to invalidate in memory cache across a distributed cluster upon a change to the authoritive store.

Hope this helps

Ehf answered 15/9, 2022 at 7:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.