这是我第六篇第六届青训营文章 minyizou.substack.com/p/understan…
ZSet
When you aim to achieve tens of millions of accesses on a single server, using Redis's zset is an excellent choice. In such scenarios, MySQL might very likely become overwhelmed and crash.
- Hash Table: The hash table stores the elements of the zset as keys and their scores as values. This allows for O(1) time complexity when fetching an element's score.
- zskiplist (Skip List) : The skip list allows for the elements to be ordered by their scores, making range queries efficient. In a skip list, elements are stored in multiple levels, allowing for faster traversal in a sorted manner. When inserting a new element, a "level" for it is chosen randomly, and the element is inserted into all levels up to the chosen one. This random assignment ensures that the skip list remains balanced, providing log(n) time complexity for search, insert, and delete operations.
Distributed Lock
The SETNX command in Redis is used to set a key's value only if the key does not already exist. The name SETNX stands for "SET if Not exists."
Redis executes commands in a single thread, and the setnx command succeeds only if it has not been set previously.
While setnx can be used as a primitive method to implement distributed locks in Redis, it's not a highly available solution for several reasons:
No Deadlock Handling: If the process that acquired the lock crashes or gets delayed, the lock might never be released. This can be mitigated using a timeout with the lock, but handling timeouts correctly can be complex. (Then you will add time out)
Failover Issues: In a Redis cluster, if the primary node fails after granting a lock but before replicating it to a secondary node, another client can acquire the same lock from the secondary after failover, leading to two clients holding the lock or no lock simultaneously.
Two masters: If vote process gets mistakes, there are two masters in the system.
Some tips in Redis Design
Large Key solutions:
To optimize Redis performance, it's advisable to avoid using large keys.
Split Value: When you have a big value associated with a key, instead of storing it as a single large chunk, consider breaking it down:
Before (as depicted by blue): Store the entire value as a single big key.
After: Split the value into smaller chunks and store them in Redis. When retrieving the value, combine these chunks back to get the original value.
This approach not only ensures better memory management and performance in Redis but also offers more granular control over individual chunks. It's a good practice to compress the value before writing it into Redis. Various compression algorithms can be employed for this purpose, including:
- gzip: A widely used compression method.
- snappy: Known for its fast compression and decompression speeds.
- lz4: Offers a balance between speed and compression ratio.
Additionally, if you are storing JSON strings, you might consider using MessagePack, which not only compresses the data but also provides a more efficient serialization format compared to traditional JSON.
Hash Modulus: By hashing the key and then taking the modulus of the result, you can divide a large key's associated data across multiple smaller keys or slots. After hashing, using a bitmask can help you extract certain bits from the hash. This can assist in further segmenting your data, allowing you to distribute it across even more keys, further reducing the individual key size.
Hot-Cold Partitioning: Large keys often have varying access patterns. Segmenting data based on its access frequency can help manage large keys efficiently. 'Hot' data, or data that is accessed frequently, can be kept in more readily accessible partitions or slots. Meanwhile, 'cold' data, which is less frequently accessed, can be moved to other partitions. This ensures that large data sets don't hinder the performance of frequently accessed data."
Hot Key solutions:
f some keys are accessed multiple times within a short period in a system like Redis, it indicates that these keys are "hot" in terms of data access patterns. There are several considerations and strategies you can employ to handle such scenarios:
-
Caching: If you're not already using Redis or another caching mechanism (Localcache), then it's time to consider it. For keys that are frequently accessed, caching can significantly improve system performance and reduce the load on primary data stores, e.g., Bitcache in Golang.
-
Sharding: If a particular set of keys is frequently accessed and causing a bottleneck, consider sharding your data. By splitting your dataset and distributing it across multiple instances or nodes, you can distribute the load and reduce contention for these hot keys.
-
Proxy:
ZSet
When you aim to achieve tens of millions of accesses on a single server, using Redis's zset is an excellent choice. In such scenarios, MySQL might very likely become overwhelmed and crash.
- Hash Table: The hash table stores the elements of the zset as keys and their scores as values. This allows for O(1) time complexity when fetching an element's score.
- zskiplist (Skip List) : The skip list allows for the elements to be ordered by their scores, making range queries efficient. In a skip list, elements are stored in multiple levels, allowing for faster traversal in a sorted manner. When inserting a new element, a "level" for it is chosen randomly, and the element is inserted into all levels up to the chosen one. This random assignment ensures that the skip list remains balanced, providing log(n) time complexity for search, insert, and delete operations.
Distributed Lock
The SETNX command in Redis is used to set a key's value only if the key does not already exist. The name SETNX stands for "SET if Not exists."
Redis executes commands in a single thread, and the setnx command succeeds only if it has not been set previously.
While setnx can be used as a primitive method to implement distributed locks in Redis, it's not a highly available solution for several reasons:
No Deadlock Handling: If the process that acquired the lock crashes or gets delayed, the lock might never be released. This can be mitigated using a timeout with the lock, but handling timeouts correctly can be complex. (Then you will add time out)
Failover Issues: In a Redis cluster, if the primary node fails after granting a lock but before replicating it to a secondary node, another client can acquire the same lock from the secondary after failover, leading to two clients holding the lock or no lock simultaneously.
Two masters: If vote process gets mistakes, there are two masters in the system.
Some tips in Redis Design
Large Key solutions:
To optimize Redis performance, it's advisable to avoid using large keys.
Split Value: When you have a big value associated with a key, instead of storing it as a single large chunk, consider breaking it down:
Before (as depicted by blue): Store the entire value as a single big key.
After: Split the value into smaller chunks and store them in Redis. When retrieving the value, combine these chunks back to get the original value.
This approach not only ensures better memory management and performance in Redis but also offers more granular control over individual chunks. It's a good practice to compress the value before writing it into Redis. Various compression algorithms can be employed for this purpose, including:
- gzip: A widely used compression method.
- snappy: Known for its fast compression and decompression speeds.
- lz4: Offers a balance between speed and compression ratio.
Additionally, if you are storing JSON strings, you might consider using MessagePack, which not only compresses the data but also provides a more efficient serialization format compared to traditional JSON.
Hash Modulus: By hashing the key and then taking the modulus of the result, you can divide a large key's associated data across multiple smaller keys or slots. After hashing, using a bitmask can help you extract certain bits from the hash. This can assist in further segmenting your data, allowing you to distribute it across even more keys, further reducing the individual key size.
Hot-Cold Partitioning: Large keys often have varying access patterns. Segmenting data based on its access frequency can help manage large keys efficiently. 'Hot' data, or data that is accessed frequently, can be kept in more readily accessible partitions or slots. Meanwhile, 'cold' data, which is less frequently accessed, can be moved to other partitions. This ensures that large data sets don't hinder the performance of frequently accessed data."
Hot Key solutions:
f some keys are accessed multiple times within a short period in a system like Redis, it indicates that these keys are "hot" in terms of data access patterns. There are several considerations and strategies you can employ to handle such scenarios:
-
Caching: If you're not already using Redis or another caching mechanism (Localcache), then it's time to consider it. For keys that are frequently accessed, caching can significantly improve system performance and reduce the load on primary data stores, e.g., Bitcache in Golang.
-
Sharding: If a particular set of keys is frequently accessed and causing a bottleneck, consider sharding your data. By splitting your dataset and distributing it across multiple instances or nodes, you can distribute the load and reduce contention for these hot keys.
-
Proxy: