Redis's String data type and structure

String type


1. String data type and structure


Introduction to String Types

String type is the most basic data structure for redis and is also the most commonly used type.The other four types are more or less built on string types, so String types are the basis of redis.

String values can store up to 512MB, where String types can be simple strings, complex xml/json strings, binary image or audio strings, and strings that can be numbers.


Common Commands


set command

Description: This command sets the value of a given key.If the key already stores other values, the SET overrides the old values regardless of the type.

127.0.0.1:6379> set name starsky
OK
127.0.0.1:6379>

The set command binds a value to the rest key value and returns OK when the SET successfully completes the set operation.


get command

Description: This command is used to get the value of the specified key.If the key does not exist, return nil.An error is returned if the key corresponds to a stored value that is not of type string.

127.0.0.1:6379> get name
"starsky"
127.0.0.1:6379>

getset command

Description: This command gets the old value of the specified key and assigns it to the new value.Returns nil when there is no old value in the key.

127.0.0.1:6379> getset name will
"starsky"
127.0.0.1:6379> get name
"will"
127.0.0.1:6379>

mget command

Description: This command returns the values of multiple keys and nil if one of the KEY values does not exist.

127.0.0.1:6379> set age 10
OK
127.0.0.1:6379> set sex 1
OK
127.0.0.1:6379> mget name age sex
1) "will"
2) "10"
3) "1"
127.0.0.1:6379>

decr command

Description: Subtract the number corresponding to the key by one.If the key does not exist, it will be set to zero before the operation.If the key has a value of the wrong type or is a string that cannot be represented as a number, an error is returned.

127.0.0.1:6379> decr age
(integer) 9
127.0.0.1:6379> get age
"9"
127.0.0.1:6379>

incr command

Description: Performs an atomic plus 1 operation on a number stored in the specified key. If the specified key does not exist, it will be set to zero before the incr operation.If the value stored in the specified key is not a string type (fix:) or the stored string type cannot be represented as an integer, the server will return an error (eq:(error) when executing this command (ERR value is not an integer or out of range).

127.0.0.1:6379> get age
"9"
127.0.0.1:6379> incr age
(integer) 10
127.0.0.1:6379>

More String commands, see here: http://www.redis.cn/commands.html#string



2. String simple character structure


SDS Dynamic String

SDS (Simple Dynamic Strings) is a basic data structure for Redis and is used primarily to store strings and integers.

SDS data structure implementation (Redis3):

struct sdshdr {
	unsigned int len;
	unsigned int free;
	char buf[];
};

Where buf represents the data space used to store strings;len denotes the number of bytes occupied by buf, or string length;free represents the number of bytes remaining in the buf.


benefit

  • With separate variables len and free, you can easily get the string length and the remaining space.
  • Content is stored in a dynamic array buf, and SDS's pointer to upper exposure points to buf, not to struct SDS.Therefore, the upper layer can read the SDS content as C string, compatible with various functions of C language processing string, and can easily get other variables through buf address offset.
  • Read-write strings do not depend on\0 for binary security.

Disadvantages

  • For strings of different lengths, it is not necessary to use two 4-byte variables len and free?
  • A 4-byte len represents a string of 2^32 in length. In practice, strings stored in Redis are often not that long, so can space be further compressed?

New SDS Structure

Redis adds a flags field to identify the type and stores it in one byte (8 bits).

Where: the first three bits represent the type of string;The remaining 5 bits can be used to store short strings less than 32 in length.

struct __attribute__ ((__packed__)) sdshdr5 {
	unsigned char flags; /* First 3-bit storage type, last 5-bit storage length */
	char buf[]; /* Dynamic arrays, storing strings */
};

For strings larger than 31, simply storing the length in the last 5 bits of flags is obviously not enough, and additional variables are needed.The data structures of sdshdr8, sdshdr16, sdshdr32, and sdshdr64 are defined as follows:

  • len represents the length used.
  • alloc represents the total length.
  • buf stores the actual content.
  • The first three bits of flags are still stored, while the last five bits are reserved.
struct __attribute__ ((__packed__)) sdshdr8 {
	uint8_t len; /* Length used, 1 byte */
	uint8_t alloc; /* Total length, 1 byte */
	unsigned char flags; /* First 3-bit storage type, last 5-bit reservation */
	char buf[];
};

struct __attribute__ ((__packed__)) sdshdr16 {
	uint16_t len; /* Length used, 2 bytes */
	uint16_t alloc; /* Total length, 2 bytes */
	unsigned char flags; /* First 3-bit storage type, last 5-bit reservation */
	char buf[];
};

struct __attribute__ ((__packed__)) sdshdr32 {
	uint32_t len; /* Length used, 4 bytes */
	uint32_t alloc; /* Total length, 4 bytes */
	unsigned char flags; /* First 3-bit storage type, last 5-bit reservation */
	char buf[];
};

struct __attribute__ ((__packed__)) sdshdr64 {
	uint64_t len; /* Length used, 8 bytes */
	uint64_t alloc; /* Total length, 8 bytes */
	unsigned char flags; /* First 3-bit storage type, last 5-bit reservation */
	char buf[];
};

Redis Create String Process

sds sdsnewlen(const void *init, size_t initlen) {
	void *sh;
	sds s;
	// Calculate corresponding type based on string length
	char type = sdsReqType(initlen);
	// Strongly converts to SDS_if a''string is createdTYPE_8
	if (type == SDS_TYPE_5 && initlen == 0) type = 	SDS_TYPE_8;
	// Calculate the length required for the head based on the type (the head contains len, alloc,	flags)
	int hdrlen = sdsHdrSize(type);
	// Pointer to flags
	unsigned char *fp;
	// String was created, +1 because of the `\0`terminator
	sh = s_malloc(hdrlen+initlen+1);
	if (sh == NULL) return NULL;
	if (init==SDS_NOINIT)
	init = NULL;
	else if (!init)
	memset(sh, 0, hdrlen+initlen+1);
	// s points to buf
	s = (char*)sh+hdrlen;
	// s minus 1 to get flags
	fp = ((unsigned char*)s)-1;
	...
	// Add\0 terminator at the end of s
	s[initlen] = '\0';
	// Return pointer s to buf
	return s;
}

The general process for creating an SDS is to first calculate the type from the string length, calculate the length required for the header from the type, and then dynamically allocate memory space.


Be careful:

  1. When creating an empty string, SDS_TYPE_5 is cast to SDS_TYPE_8 (because the content may be updated frequently after an empty string is created, causing an expansion operation, so it is created directly as sdshdr8).
  2. The length calculation has a + 1 operation because the terminator\0 takes up a long space.
  3. Returns pointer s to buf.


Application of String Data Type


session sharing

As follows:

A distributed web service records the user's Session information (e.g., login information) to their servers, which creates a problem that, under load balancing, servers balance user access to different servers and users may find that they need to log in again when they refresh their access. This problem is intolerable to the user experience.

To solve this problem, we will use Redis to centrally manage the user's Sessions, so that only the high availability and scalability of redis are guaranteed. Sessions are retrieved from Redis for each user's login or query login.

As follows:



Counter

For example, the flow of a commodity;The following code shows:

<?php
// Assembly Connection Information
$config = [
    "redis" => [
        "host" => '192.168.188.190',
        "port" => 6379,
        "password" => "root" //Password needs to be set on its own profile
    ]
];

// Instantiate Object
$Redis = new Redis();

// Redis connections using functions
$Redis->connect($config['redis']['host'],$config['redis']['port']);

// Input password
$Redis->auth($config['redis']['password']);

// Commodity ID
$key = "product:".$_GET['product_id'];

// Judging that $key commodity ID does not exist
if (!$Redis->exists($key)){

	// key for newly generated commodity ID
	$Redis->set($key,1);
	
}else{  // Otherwise, it exists

	// Increase the flow of ID key by 1 
	$Redis->incr($key);
	
}

// Jump to the corresponding item details
header("Location:http://blog-login.com/view/SteelSeries_zxr.php?id=".$_GET['id']."&product_id=".$_GET['product_id']);

?>

Redis Speed Limit

In some projects, to ensure security, users are required to enter their mobile phone number for authentication when they log on, but there are some restrictions to ensure that the SMS interface is not frequently accessed.

<?php
// Connect Redis
$Redis = new Redis("192.168.29.108",6379);

// Connection Password
$Redis->auth("root");

// Requested Mobile Number
$phonename="176xxxx0888";

// Define a key
$key = "info:".$phonename;

// Check if this key exists
$restful = $Redis->exists($key);

// Judge that key is empty or less than 5 times
if ($restful != null || $Redis->incr($key)<=5) {

	return "OK"; // Send verification information

}else { // otherwise

	echo "1 Minute cannot request 5 times"; // Return error information

}
?>

The code above uses Redis for speed limits, such as restricting an IP address from being accessed more than n times in a second on some websites.




Keywords: Redis

Added by sarbas on Mon, 06 Sep 2021 19:59:20 +0300