Почему для постоянного хранения данных не используют только оперативную память (RAM), а применяют дисковые СУБД?

Ответ

Хотя оперативная память (RAM) чрезвычайно быстра, использовать её как единственное средство для постоянного хранения данных нецелесообразно и небезопасно по нескольким причинам. Важно понимать, что RAM активно используется для кеширования, но не как основное хранилище.

Ключевые причины:


  1. Энергозависимость (Volatility)

    Это главная проблема. Данные в RAM полностью теряются при отключении питания или перезагрузке сервера. Это нарушает ключевой принцип D (Durability / Долговечность) из акронима ACID, который гарантирует, что после успешного завершения транзакции данные не будут потеряны.



  2. Ограниченный объем и высокая стоимость

    Стоимость одного гигабайта RAM на порядки выше стоимости гигабайта на SSD или HDD. Обеспечить хранение больших объемов данных (терабайты) в оперативной памяти очень дорого.



  3. Сложность обеспечения целостности

    Дисковые СУБД имеют отлаженные механизмы (например, журнал упреждающей записи — WAL), которые гарантируют атомарность и целостность данных даже в случае сбоя посреди операции. Реализовать аналогичные гарантии в in-memory решении без записи на диск крайне сложно.


In-Memory базы данных (Redis, Tarantool)
Существуют СУБД, которые хранят данные преимущественно в RAM. Однако для обеспечения долговечности они используют механизмы персистентности: периодическое сохранение снимков (снапшотов) на диск или ведение лога команд (AOF), который также пишется на диск.

Пример: потокобезопасный кеш в Go
В приложениях RAM используется для кеширования данных, полученных из медленного хранилища (БД, внешний API). Вот пример правильной реализации кеша в Go с защитой от гонки данных:

import "sync"

// Cache - потокобезопасная структура для кеширования
type Cache struct {
    mu    sync.RWMutex
    items map[string]interface{}
}

func NewCache() *Cache {
    return &Cache{
        items: make(map[string]interface{}),
    }
}

// Set - потокобезопасная запись в кеш
func (c *Cache) Set(key string, value interface{}) {
    c.mu.Lock() // Блокируем на запись
    defer c.mu.Unlock()
    c.items[key] = value
}

// Get - потокобезопасное чтение из кеша
func (c *Cache) Get(key string) (interface{}, bool) {
    c.mu.RLock() // Блокируем на чтение (допускает параллельные чтения)
    defer c.mu.RUnlock()
    item, found := c.items[key]
    return item, found
}