Redis in go (一)| 青训营

113 阅读2分钟

这是我第五篇第六届青训营文章

Read and write in Redis

Typically, we utilize MySQL for reading and writing data. But when we need faster access to frequently viewed data due to certain challenges, we consider using Redis. When the server issues a read request, it first accesses Redis. If the corresponding data is not found, it then queries MySQL. When the server issues a write request, it first writes to MySQL, then uses various tools to listen to binlog, subsequently updating Redis.

Redis client sends RESP, it will save tables in memory and write those data in disk. Data is saved to the hard drive to prevent data loss upon restart. Incremental data is saved to an AOF (Append Only File). When the server restarts, any unexecuted commands in Redis will be replayed. RDB captures a snapshot of Redis's dataset. Upon restart, the RDB file is read first. Then, it checks if there are any unexecuted commands after the RDB file. If there are, the unexecuted commands from the AOF are loaded and executed.

Redis processes all operation commands using a single thread:

String Data Structure in Redis

SDS, or Simple Dynamic String, is a data structure used internally by Redis to represent strings.

  • An SDS is not just a sequence of characters like a regular C string. Instead, it's a structure that contains metadata about the string.
  • This structure contains:

    • The length of the string.
    • The amount of free space (unused bytes) at the end of the string.
    • The actual byte array of characters.

Advantages:

a. Constant Time Length Lookup:

  • Thanks to the length field in the SDS header, Redis can determine the length of an SDS string in constant time (O(1)). In contrast, finding the length of a regular C string would require traversing the entire string until the null-terminator is encountered, which takes linear time (O(n)) for a string of n characters.

b. Safe against Buffer Overflows:

  • Because the SDS structure keeps track of its own length and allocated space, operations that manipulate the string are less likely to result in buffer overflows compared to traditional C strings, where you might unintentionally overwrite adjacent memory.

c. Efficient Appends:

  • The free space field allows SDS to perform efficient append operations. If there's enough free space at the end of the string, appending to an SDS can be done without reallocating and copying memory, making it a much faster operation than it would be with traditional strings.

d. Binary Safe:

  • Unlike C strings, which are null-terminated and can't handle embedded null bytes, SDS strings can store any binary data, including null bytes. This makes them suitable for storing not just text but any binary data.

Quicklist in Redis

Quicklist is a specialized data structure used internally by Redis to optimize the memory representation of lists. Redis lists can be implemented using either linked lists or ziplists. Both representations have their own pros and cons. To get the best of both worlds, Redis introduced the quicklist, which is essentially a linked list of ziplists.

Combining doubly linked lists with listpacks, Redis is able to optimize for both memory usage and operational efficiency across different list sizes. The totbytes field tracks the total size of the underlying memory structure, listpack can save more than one element in one entry.

How to reduce the latency from the client round-trip time

Instead of sending a command and waiting for a response repeatedly, you send multiple commands at once and then read all the responses.

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/go-redis/redis/v8"
)

func main() {
	rdb := redis.NewClient(&redis.Options{
		Addr: "localhost:6379",
	})
	defer rdb.Close()

	ctx := context.Background()
	
	// Check the connection
	_, err := rdb.Ping(ctx).Result()
	if err != nil {
		log.Fatalf("Could not connect to Redis: %v", err)
	}

	// Start a pipeline
	pipe := rdb.Pipeline()

	incr := pipe.Incr(ctx, "mycounter")
	pipe.Expire(ctx, "mycounter", time.Hour)

	// Execute all commands in the pipeline
	_, err = pipe.Exec(ctx)
	if err != nil {
		log.Fatalf("Pipeline failed: %v", err)
	}

	fmt.Println(incr.Val())
}

Code example: gitee.com/wedone/redi…