The article directories

  • Introduction to the
  • Redis common command
  • String
  • Hash
  • List
  • Set
  • ZSet
  • Redis transactions
  • Redis primary/secondary replication
  • Redis subscribe to publish

Writing background

Redis has learned it for some time, and has written many blogs. Recently, in the interview, I found that many companies have requirements, and there are questions, when asked, I feel a little forgotten, when reviewing the previous blog, it was a little messy and difficult to review, so this article came into being.

Introduction to the

Redis (Remote Dictionary Server) is an open-source, network-enabled, memory-based, key-value database written in C language, which can be used for caching. For example, when we want to query data from the database, Redis (Remote Dictionary Server) can be used for caching. We can first through Redis, if there is in Redis, then directly read the data in Redis, if there is no, then go to the database query, and put the query results into Redis, in order to facilitate the next direct access, so as to reduce the pressure of the database, and improve the efficiency of the query, Because Redis data is in memory, it is faster than a database. This article focuses on the use and underlying implementation of Reids.

Redis common command

  • Set key-value pairs

    set myKey abc
  • Gets key value pairs

    get myKey
  • View all keys

    keys *
  • Delete keys. You can delete multiple keys

    del key [key ...]
  • Check whether a key exists

    exists key
  • Check the remaining validity period of the key. -1 indicates permanent, and -2 indicates invalid

    ttl key
  • Set the validity time of the key, in seconds

    expire key seconds
  • Set the key to permanent

    persist key
  • Check the keys that meet the requirements. * indicates all keys.? Represents a character

    keys pattern
  • Redis has 16 databases by default, with subscripts from 0 to 15. The default is in the 0 database

    select index
  • Returns a random key from the current database

    randomkey
  • Change the name of the key

    rename key newkey
  • Moves a key from the current database to the specified database

    move key db
  • Check the key type

    type key

String

The String type is the most basic data type of Redis. One key can store up to 512MB

  • Set is used to set a value for the key. If the key already has a value, set overwrites the old value

    set key value
  • Gets the value of the specified key, returns nil if key does not exist, or an error if key is not stored as a string

    get key
  • Set the key value only if the key does not exist

    setnx key value
  • Gets a substring of the string in the specified key

    getrange key start end
  • Returns the length of the string stored by key

    strlen key
  • Increment the numeric value stored in the key by 1. If the key does not exist, the value of the key is initialized to 0 and then incremented by 1

    incr key
  • Increments the specified key to a user-defined value

    incrby key increment
  • Decrement the number stored in the key by 1

    decr key
  • Decrement a user-defined value from the specified key

    decrby key decrement

    Simple Dynamic String (SDS)

The bottom layer is an SDS, which contains three parts

char buf[]; // A byte array to hold the string int len; // The number of bytes used in the buf array, that is, the length of the SDS string int free; // Number of unused bytes in the buf arrayCopy the code

Since C string does not record its own length information, it must traverse the entire string when obtaining the length of C string, and its time complexity is O(n). SDS has len attribute, so the time complexity is O(1) when obtaining the length of C string. Buffer overflow Due to the C string does not record the length of the string, when we conduct string concatenation, buffer overflow problems may arise, for example, we have the s1 and s2 two strings, when we want to put the s2 string concatenation to s1 string, if before joining together not allocated enough space to s1, Then the data of S1 will overflow into the space of S2, resulting in the destruction of the original data of S2. SDS will first check whether the space is sufficient when performing modification operations. If not, it will automatically expand to solve the problem of buffer overflow. Memory redistribution number for C string, when for string concatenation operation, need to pass the memory redistribution to extend the space at the bottom of the array size, if not extending, probably can appear buffer overflow, when Venus string to shorten, also need to be memory redistribution, that part to release the unused memory, if forget this step, Memory leaks can occur. As you can see, either string concatenation or string shortening requires memory reallocation. For example, if we have s1=”hello” and S2 =” SDS “, two strings, take S1 as the research object, len of S1 is 5, free is 0, when S2 is joined to S1, memory is reallocated. Len of S1 becomes 8, and free becomes 8. This is preallocation, and the next time you concatenate the “world” string, since there is enough free space, you can allocate it directly without reallocating memory. When we shorten the string, SDS uses lazy space free, if we have a string s= “helloSDS”, len is 8, free is 0, when we truncate SDS, len is 5, free is 3, that is, these three Spaces are not freed by memory reallocation. In this way, the number of memory reallocation is reduced and the efficiency is improved. It should be noted that the free space is not unlimited. The free space is limited to 1MB. Binary safe C string coding is the ASCII coding, end at the end of the string is \ 0, character is empty, so cannot contain null character in the string, otherwise will let the program took over, which also limits the C string can only save the text data, can’t save pictures, audio, video and other binary data. SDS can not only store text data, but also store other data in the buF array in binary mode. The program does not have any restrictions on data, and what is written is read. In this way, not only text can be stored, but also binary data of any format can be stored. SDS compatible with some C string functions ALTHOUGH SDS is binary safe, but it also follows the convention of C string with empty string solution, so SDS can reuse some functions of C string.

Hash

Hash is especially good for storing objects, that is, our Javabeans. Each Hash can store 2^32-1 key-value pairs, which can be regarded as a map of keys and values

  • Set field/value for the specified key. Key can be interpreted as a row of data in a database table, field can be interpreted as a field, and value is a value

    hset key field value
  • You can set multiple keys corresponding to keys in the hash table

    hmset key field value[filed value ...]
  • Get the value stored in the hash, based on the field

    hget key field value
  • Gets multiple fields for the current key

    hmget key field value
  • Gets all the fields in the hash table

    hkeys key
  • Gets the number of fields in the hash table

    hlen key
  • Gets all fields and values corresponding to the key

    hgetall key
  • Delete one or more fields (that is, fields in our database)

    hdel key field
  • Set the hash field value only if field does not exist

    hsetnx key field value
  • Increment the integer value of the specified field in the hash table

    hincrby key field increment
  • Check whether the specified field in the hash table exists

    hexists key field

At the bottom of Hash are ziplist and Hashtable. We’ve already talked about compressed lists, but here we’ll focus on Hashtable. Compressed lists Compressed lists are used when data is scarce and are stored in pairs (key-value pairs), meaning that they occupy two consecutive positions in a compressed list instead of two compressed lists. HashTable A hash table contains an array of hash tables, hash table sizes, and hash table size masks that are used to calculate index values and hash table nodes

type struct dictht {
dictEntry **table;
unsigned long size;
unsigned long sizemask;
unsigned long used;
}dictht;
Copy the code

Table is an array of hash tables in which each element holds a pointer to a key-value pair. When there are multiple keys for the same key, resolve the conflict using chained address method (see what Hash table is for details), and when there is a lot of data, use a skip table, which is basically equivalent to a red-black tree.

List

A List in Redis is a simple List of strings, sorted by insertion order, with one element added to the top or bottom of the List. A List can contain up to 2^ 32-1 elements, similar to a LinkedList in Java

  • Inserts one or more values into the head of the list, added from the left

    lpush key value1 [value2]
  • Inserts one or more values to the end of the list, adding them from the right

    rpush key value1 [value2]
  • Iterate over a list from the start index to the stop index, with -1 representing the last index

    lrange key start stop
  • Gets the length of the list

    llen key
  • Gets the value of a list element by index

    lindex key index
  • Removes the first element from the left list, returning the value of the deleted element

    lpop key
  • Removes the first element of the list from the right

    rpop key
  • Removes and retrieves the first element of the list. If there is no element in the list, the list is blocked until the wait times out or an eject element is found, in seconds

    blpop key timeout
  • Removes and retrieves the last element of the list. If there is no element in the list, the list is blocked until the wait times out or an eject element is found

    brpop key timeout
  • Trims the list to keep only elements in the specified range

    ltrim key start stop
  • Set the value of the specified element by index

    lset key index value
  • Inserts values before or after the specified element

    linsert key before | after privot value
  • Removes the last element from the first list and adds it to the first position in the second list

    rpoplpush source destination
  • Move the last element in this list to the first position

    rpoplpush source destination

The bottom implementation of a List is ziplist and linkedList. When the List key (value) contains fewer items, the bottom implementation of a List is ziplist and linkedList. Zltail occupies 4 bytes of memory for the compressed list, zllen occupies 2 bytes for the distance from the end of the compressed list to the table head, and entryx occupies an indefinite number of bytes for the number of nodes in the compressed list. Zlend occupies 1 byte for each node of the compressed list (which can be byte arrays and integer values). Mark compression footer compression list like arrays, has carried on the packaging can be as simple as the array, contains inside, some of the properties, like zlend attribute list implementation each node of the underlying implementation is given below

typedef struct listNode { struct listNode *prev; struct ListNode *next; void *value; // Save the value of the node}listNode;Copy the code

The bottom layer of list is multiple linked list nodes together, in addition, list also provides some inherent properties, easy to operate, the following is a simple bottom layer implementation of list

typedef struct list { listNode *head; // This pointer points to the listNode *tail; // The pointer to the end of the linked list is unsigned long len; Void *(*dup) (void * PTR); void (*free) (void *ptr); void (*match) (void *ptr,void *key); }list;Copy the code

The time complexity of list is O(1) when it operates the head, tail and length of the linked list. The LIST also encapsulates dUP, free and match functions, which are used to copy the value saved by the linked list node, release the value saved by the linked list node, and compare the value saved by the linked list node with another input value.

Set

The Set of Redis is an unordered Set of String type, whose members are unique and cannot be repeated. The underlying data structure is Intset and Hashtable. Intset can be understood as an array, which is ordered

  • Adds a value to the specified collection

    sadd key member
  • Gets the size of the collection

    scard key
  • Gets all values of the collection, unordered

    smembers key
  • Check whether the member element is a member of the collection key

    sismember key member
  • Returns one or more random set values. Count may not be written

    srandmember key [count]
  • Removes one or more members of a collection

    srem key member1 [member2]
  • Remove a random count of elements and return the count of elements. Default is 1

    spop key [count]
  • Moves an element from the first collection to the second collection

    smove source destination member
  • The difference between the first set and the second set, whichever is the first set

    sdiff key [key...]
  • Intersection of sets

    sinter key [key...]
  • And set

    sunion key [key...]

    At the bottom are integer sets (intSets) and Hashtables

    The integer set
typedef struct intset { uint32_t encoding; uint32_t length; int8_t contents[]; }intset;}intset;}intset;}intset;Copy the code

Underneath it is an array whose elements cannot be repeated.

ZSet

The ZSet set in Redis is a set of String elements and cannot be repeated, except that each element is associated with a float score. The score is used to sort the members of the set from smallest to largest, and can be repeated

  • Adds one or more ordered collections that have a score for sorting

    zadd key score member
  • Gets the number of members in an ordered collection

    zcard key
  • Gets the values of the specified range collection, from low to high by default

    zrange key start stop
  • Gets the number of collection elements for the specified range score

    zcount key min max
  • Returns the index of the specified element

    zrank key member
  • Gets the values of the specified range collection, from high to low

    zrevrange key start stop
  • Removes one or more specified elements from the collection

    zrem key member [member...]
  • Removes all members of a given rank range from the ordered collection, ranking from lowest to highest by default, with the first rank being 0

    zremrangebyrank key start stop
  • Removes a member of the fractional interval in an ordered set

    zremrangebyscore key min max

    At the bottom are Ziplisst and Skiplist, which are compressed lists and skip lists

    The special feature of Zset is that you can sort by score.

Redis transactions

Does Redis support transactions? Before demonstrating transactions, it’s important to look at these commands. Multi Open transaction exec execute transaction discard Cancel transaction watch key The following illustrates a transaction with a transfer example.

127.0.0.1:6379> multi OK 127.0.0.1:6379> set money QUEUED 127.0.0.1:6379> get money QUEUED 127.0.0.1:6379> exec 1) OK 2) "100"Copy the code

This is a normal transaction problem, where you start a transaction, there’s a set of data in the transaction, and then you execute, and each instruction here is put in a queue, and when it’s executed, it’s executed one by one. The following demonstrates a cancellation transaction

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set money 100
QUEUED
127.0.0.1:6379> get money
QUEUED
127.0.0.1:6379> discard
OK
Copy the code

This is normally cancelled if we get a syntax error while executing a transaction, then the entire transaction will not be executed

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set money 100
QUEUED
127.0.0.1:6379> sett age 20
(error) ERR unknown command `sett`, with args beginning with: `age`, `20`, 
127.0.0.1:6379> get money
QUEUED
127.0.0.1:6379> exec
(error) EXECABORT Transaction discarded because of previous errors.
Copy the code

As you can see, when there is a syntax error, the entire transaction is not executed, and there is another kind of error, not a syntax error, but a runtime error.

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set name "hzy"
QUEUED
127.0.0.1:6379> incr name
QUEUED
127.0.0.1:6379> get name
QUEUED
127.0.0.1:6379> exec
1) OK
2) (error) ERR value is not an integer or out of range
3) "hzy"
Copy the code

We let a string increment and got an error, but the rest of the transaction executed. Another situation is that when we are executing a transaction, the exec has not yet been executed, and then some other client interferes.

127.0.0.1:6379> multi OK 127.0.0.1:6379> set money QUEUED 127.0.0.1:6379> incr money QUEUED 127.0.0.1:6379> exec 1) OK 2) (integer) 101Copy the code

If, prior to exec, another client does the following

127.0.0.1:6379> set money 500
OK
Copy the code

Then client 1 performs exec, and we get

127.0.0.1:6379 > get money "101"Copy the code

As you can see, this is the result of the completion of the transaction, and the 500 did not succeed. To solve this problem, a lock appears, which is monitored with watch, which is our optimistic lock.

127.0.0.1:6379> watch money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set money 100
QUEUED
127.0.0.1:6379> incr money
QUEUED
127.0.0.1:6379> exec
(nil)
127.0.0.1:6379> get money
"500"
Copy the code

Here we monitor money and perform the following on client 2 before executing exec

127.0.0.1:6379> set money 500
OK
Copy the code

Then exec returns nil because money is watched. When we get money again, it will be 500 and the transaction will not be executed. Note that monitoring is automatically unmonitored when we execute exec, discard, or unwatch.

Redis primary/secondary replication

In Redis, you can run the slaveof command or set the Slaveof option to have one server copy another server to achieve synchronization. The replicated server is called the master server, and the replicated server is called the slave server. The master server is responsible for writing and the slave server for reading.

For the demonstration of master/slave replication, we need to prepare two Redis servers. For simplicity, I configured two Redis servers with different port numbers on my Ali Cloud, and the other one with port number 6380

These four things need to be changed when configuring the serverStart as a daemondaemonize yesAnd the port numberport 6380, process filepidfile /var/run/redis_6380.pidTo persist filesdbfilename dump6380.rdb



Next we will connect the client to 6380 server, remember to take the -p parameter, not the default connection to 6379

./redis-cli -p 6380



After connecting to server 6380, we set the primary server for that server to be 6379

SLAVEOF 127.0.0.1 6379



Server 6380 is a slave server whose primary port number is 6379

INFO replication



Below is the information for the master server, which has a slave server



Let’s set a value for the primary server



As you can see, we can get it directly from the slave server, which is master-slave replication



An error is reported when setting values on the slave server because the slave server only reads, not writes



When our master server went down, we could see that the slave server or slave server could only do read work





When the master server downtime, from service cannot act as the primary server, lead to can’t finish write operation, if you want to responsible for write operation from the server, and the need to manually copy, more troublesome, hence the emergence of a sentry mode, sentry can monitor, if the primary server is down, if the downtime, chosen from the server a, when the family server

Sentinel is also a process and has a sentinel.conf configuration file where we can configure the servers it monitors. There is a sentry configured to monitor port 6379. When this server goes down, the primary server needs to be elected from the secondary server. This sentry has two votes (multiple sentries can be configured because sentries can also be assassinated).

Sentinel Monitor MyMaster 127.0.0.1 6379 2



So what we’re gonna do isOne primary server, two secondary servers

Next we start the Sentinel, and you can see that the primary server monitored by the Sentinel is 6379, as well as two secondary servers

./redis-sentinel .. /sentinel.conf



Next, we shut down the main server, which means that the main server is down, leaving 6380, 6381, and 26379 sentinel



When you try Info Replication again after about 30 seconds, you will find that a new primary server has been elected by the sentinels.



You can see that 6381 is selected as the primary server.



When our server 6379 comes back, we can see that the primary server is 6381, which has been newly elected, and 6379 can only be used as a secondary server.

Redis subscribe to publish

Introduction to the

First of all, we need to know that when a user subscribes to a certain channel, he will always listen to whether the channel sends messages, which is a kind of blocked state. Therefore, we implement the subscription function with multiple threads, and assign a thread to each subscriber.

news



Publish the message method is relatively simple, we just need to specify which channel to publish what content on the line, here I wrapped them in a util class, using static method, easy to use.

public static void publishMsg(String channel, String message) { // Jedis jedis = getJedis(); Jedis Jedis = new Jedis("127.0.0.1",6379); try { jedis.publish(channel, message); } catch (Exception e) { System.out.println(e.getMessage()); } finally { jedis.close(); }}Copy the code

Subscription publishing This class is used to handle subscriptions, that is, what we do when we subscribe to a channel, what we do when we unsubscribe from a channel, what we do when we receive a message. Because I’ve wrapped them all under the util class, I’m going to use the inner class.

public static JedisPubSub jedisPubSub = new JedisPubSub() { @Override public void onMessage(String channel, String message) {system.out.println (" received "+ channel +" channel: "+ message); System.out.println(channel + ":" + message); } @Override public void onSubscribe(String channel, Int subscribedChannels) {system.out.println (" subscribe "+ channel +" channel "); System.out.println(channel + ":" + subscribedChannels); } @Override public void onUnsubscribe(String channel, Int subscribedChannels) {system.out.println (" unsubscribe "+ channel +" channel "); System.out.println(channel + ":" + subscribedChannels); }};Copy the code

Subscribe to the channel



Subscription channel, we only need to specify the subscribed to which channel, it is important to note here, we also need to specify, when making operation is related to the channel (subscribe, unsubscribe, receive news channel, etc.) we need how to deal with, it is used to the class above, of course, because this is a blocked thread, so use multithreading.

/** * Receive the message. * After this method is called, it is executed forever. When a corresponding message is published, it is received in jedisPubSub! * @param channels */ public static void subscribeMsg(String channels) { // Jedis jedis = getJedis(); Jedis Jedis = new Jedis ("127.0.0.1", 6379); new Thread(()->{ try { jedis.subscribe(jedisPubSub, channels); } catch (Exception e) { System.out.println(e.getMessage()); } finally { // jedis.close(); } }).start(); }Copy the code

To ensure that the publication is after the subscription, sleep here for 100 milliseconds

public static void main(String[] args) {
        JedisUtil.subscribeMsg("test");
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        JedisUtil.publishMsg("test","hello");
        JedisUtil.publishMsg("test","hello aaa");
}
Copy the code