目录
1、Redis中String字符串
- 概念
- String 是 Redis 最基本的类型,可以理解成与 Memcached 一模一样的类型,一个 key对应一个 value;
- String 类型是二进制安全的。意味着 Redis 的 string可以包含任何数据。比如jpg图片或者序列化的对象;
- String 类型是 Redis 最基本的数据类型,一个 Redis 中字符串 value 最多可以是 512M;
-
命令使用
-
1.1 常用命令解释
1. set : 添加键值对到数据库中。
- nx: 仅当数据库中不存在该键时,才能设置键值对。
- xx: 仅当数据库中已存在该键时,才能设置键值对。与 nx 参数互斥。
- ex: 设置键的过期时间(秒)。
- px: 设置键的过期时间(毫秒)。与 ex 参数互斥。
示例:
set mykey myvalue nx ex 60
2. get : 获取指定键的值。
示例:
get mykey
3. append : 将给定的值追加到原值的末尾。
示例:
append mykey " additional text"
4. strlen : 获取键值的长度。
示例:
strlen mykey
5. setnx : 仅当键不存在时,设置键的值。
示例:
setnx mykey myvalue
6. incr : 将键中储存的数字值加1。只能对数字值操作,如果键不存在,则设置初始值为1。
示例:
incr mycounter
7. decr : 将键中储存的数字值减1。只能对数字值操作,如果键不存在,则设置初始值为-1。
示例:
decr mycounter
8. incrby key num:对数字定义自增数量
只能对数字值操作,如果为空,新增值为-1; incrby/decrby <key><步长>将 key 中储存的数字值增减。自定义步长;
1.2 原子性
redis是单线程操作,并不会被打乱;
所谓原子操作是指不会被线程调度机制打断的操作;
这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch(切换到另一个线程);
(1)在单线程中能够在单条指令中完成的操作都可以认为是"原子操作",因为中断只能发生于指令之间;
(2)在多线程中,不能被其它进程(线程)打断的操作就叫原子操作;
Redis 单命令的原子性主要得益于 Redis 的单线程;
1.3 具有原子性的常用命令
以下是 Redis 中多个键值操作及范围操作的命令和解释,这些命令都具备原子性(即操作要么全部成功,要么全部失败),确保数据一致性和安全性。
- mset <key1> <value1> <key2> <value2> ...: 同时设置一个或多个键值对。
- 如果某个键设置失败,则所有键值对都不会被设置。
- 示例:
mset key1 value1 key2 value2
- mget <key1> <key2> <key3> ...: 同时获取一个或多个键的值。
- 返回顺序与提供的键顺序一致。
- 示例:
mget key1 key2 key3
- msetnx <key1> <value1> <key2> <value2> ...: 同时设置一个或多个键值对,仅当所有给定键都不存在时。
- 如果某个键已经存在,则所有键值对都不会被设置。
- 示例:
msetnx key1 value1 key2 value2
- getrange <key> <start> <end>: 获取指定键值的部分字符串,类似于 Java 中的
substring
,前包后包。- 示例:
getrange mykey 0 4 # 获取从索引0到4的子字符串
- setrange <key> <offset> <value>: 从指定位置开始,用新的值覆写原值。
- 示例:
setrange mykey 6 "redis" # 从索引6开始,用"redis"覆盖原值
- setex <key> <seconds> <value>: 设置键值的同时,设置过期时间(单位:秒)。
- 示例:
setex mykey 60 "value" # 设置键mykey的值为"value",并在60秒后过期
- getset <key> <value>: 设置新的值,并返回旧值。
- 示例:
getset mykey "newvalue" # 设置mykey的新值为"newvalue",并返回旧值
1.4 String数据结构
String 的数据结构为简单动态字符串(Simple Dynamic string,缩写 SDS)。是可以修改的字符串,内部结构实现上类似于 Java的 ArrayList,采用预分配冗余空间的方式来减少内存的频繁分配;
如图中所示,内部为当前字符串实际分配的空间 capacity 一般要高于实际字符串长度len。当字符串长度小于 1M时,扩容都是加倍现有的空间,如果超过 1M,扩容时一次只会多扩 1M的空间。需要注意的是字符串最大长度为 512M。
2、Redis常用数据类型-List列表
2.1 概念
list列表是单键多值;
Redis 列表是简单的字符串列表,按照插入顺序排序。可以添加一个元素到列表的头部(左边)或者尾部(右边)
它的底层实际是个双向链表,对两端的操作性能很高,通过索引下标的操作中间的节点性能会较差;
2.2 常用命令
1. lpush <key> <value1> <value2> ...: 从左边插入一个或多个值到列表中。
lpush mylist value1 value2 value3
2. rpush <key> <value1> <value2> ...: 从右边插入一个或多个值到列表中。
rpush mylist value1 value2 value3
3. lpop <key>: 从左边移出并返回列表中的第一个值。
lpop mylist
4. rpop <key>: 从右边移出并返回列表中的最后一个值。
rpop mylist
5. rpoplpush <source> <destination>: 从源列表右边移出一个值,并将该值插入到目标列表的左边。
rpoplpush mylist1 mylist2
6. lrange <key> <start> <stop>: 按照索引范围获取列表中的元素(从左到右)。
索引从0开始,-1表示最后一个元素。
lrange mylist 0 -1 # 获取所有元素
7. lindex <key> <index>: 按照索引获取列表中的元素(从左到右)。
lindex mylist 0 # 获取第一个元素
8. llen <key>: 获取列表的长度。
llen mylist
9. linsert <key> before|after <pivot> <value>: 在指定值的前面或后面插入新值。
linsert mylist befor epivotValue newValue
linsert mylist after pivotValue newValue
10. lrem <key> <count> <value>: 从列表中移除指定数量的值(从左到右)。
- count > 0:从头到尾移除 count 个值。
- count < 0:从尾到头移除 count 个值。
- count = 0:移除所有匹配的值。
lrem mylist 2 value # 移除从左到右第一个和第二个匹配的值
11. lset <key> <index> <value>: 将列表指定索引的值替换成新值。
lset mylist 1 newValue # 将索引为1的值替换为newValue
2.3 数据结构
List 的数据结构为快速链表 quickList;
- 首先在列表元素较少的情况下会使用一块连续的内存存储,这个结构是 ziplist,也即是压缩列表;
- 它将所有的元素紧挨着一起存储,分配的是一块连续的内存;
- 当数据量比较多的时候才会改成 quicklist;
- 因为普通的链表需要的附加指针空间太大,会比较浪费空间。比如这个列表里存的只是 int 类型的数据,结构上还需要两个额外的指针 prev 和 next;
Redis 将链表和 zipList结合起来组成了 quicklist。也就是将多个 ziplist 使用双向指针串起来使用。这样既满足了快速的插入删除性能,又不会出现太大的空间冗余;
3、Redis常用数据类型-Set集合
3.1 概念
- Redis set对外提供的功能与 List 类似是一个列表的功能,特殊之处在于 set 是可以自动排重的,当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择,并且 set 提供了判断某个成员是否在一个 set集合内的重要接口,这个也是 List所不能提供的;
- Redis 的 Set是 string类型的无序集合。它底层其实是一个value为 nuil的 hash表,所以添加,删除,查找的复杂度都是 O(1);
- 一个算法,随着数据的增加,执行时间的长短,如果是 O(1),数据增加,查找数据的时间不变;
3.2 常用命令
1. sadd <key> <value1> <value2> ...: 将一个或多个元素加入到集合中,已经存在的元素将被忽略。
sadd myset value1 value2
2. smembers <key>: 取出该集合的所有值。
smembers myset
3. sismember <key> <value>: 判断集合中是否包含该值,存在返回1,不存在返回0。
sismember myset value1
4. scard <key>: 返回集合的元素个数。
scard myset
5. srem <key> <value1> <value2> ...: 删除集合中的一个或多个元素。
srem myset value1 value2
6. spop <key>: 随机从集合中移出并返回一个值。
spop myset
7. srandmember <key> <n>: 随机从集合中取出 n 个值,不会从集合中删除。
srandmember myset 2
8. smove <source> <destination> <value>: 将集合中的一个值从一个集合移动到另一个集合。
smove myset1 myset2 value1
9. sinter <key1> <key2> ...: 返回一个或多个集合的交集元素。
sinter myset1 myset2
10. sunion <key1> <key2> ...: 返回一个或多个集合的并集元素。
sunion myset1 myset2
11. sdiff <key1> <key2> ...: 返回第一个集合与其他集合的差集元素(即在第一个集合中但不在其他集合中的元素)。
sdiff myset1 myset2
3.3 数据结构
- Set数据结构是dict字典,字典是用哈希表实现的;
- Java中的HashSet的内部实现使用的是HashMap,只不过所有的value都指向同一个对象;
- Redis的set结构也是一样,它的内部也使用hash结构,所有的value都指向同一个内部值;
4、Redis常用数据类型-Hash哈希
4.1 概念
- Redis hash 是一个键值对集合;
- Redis hash 是一个 string类型的 field 和 value 的映射表,hash 特别适合用于存储对象;
- 类似 Java 里面的 Map<String, Object>;
- 用户 ID 为查找的 key,存储的 value 用户对象包含姓名,年龄,生日等信息,如果用普通的 key/value 结构来存储;
附加:redis的hash数据类型是默认的吗?多少大小的值会被分为ziplist,多少大小的值会被分为hashtable?
- 在Redis中,hash数据类型的默认行为是当hash的field和value的数量小于64个,并且所有field和value的大小都小于64字节时,hash会使用ziplist进行存储。否则,当hash的field和value的数量超过64个或者任意field或value的大小超过64字节时,hash会使用hashtable进行存储。
- 需要注意的是,这里提到的64个和64字节都是默认值,可以通过修改配置文件或者使用命令进行调整。
4.2 常用命令
1. hset <key> <field> <value>: 给哈希表中的字段赋值。
hset: 将指定哈希表中的字段设置为指定值。如果字段已存在,其旧值将被覆盖。
hset myhash field1 value1
2. hget <key> <field>: 从哈希表中取出指定字段的值。
hget: 返回哈希表中指定字段的值。如果字段不存在,则返回 nil。
hget myhash field1
3. hmset <key> <field1> <value1> <field2> <value2> ...: 批量设置哈希表的字段值。
hmset: 一次性设置多个字段及其值到哈希表中。如果某个字段已存在,其旧值将被覆盖。
hmset myhash field1 value1 field2 value2
4. hexists <key> <field>: 检查哈希表中指定的字段是否存在。
hexists: 检查哈希表中指定字段是否存在。如果存在,返回1;否则返回0。
hexists myhash field1
5. hkeys <key>: 列出哈希表中的所有字段。
hkeys myhash
6. hvals <key>: 列出哈希表中的所有值。
hvals myhash
7. hincrby <key> <field> <increment>: 为哈希表中指定字段的值加上增量。
hincrby: 将哈希表中指定字段的值加上指定增量。如果字段不存在,则将其值初始化为0,然后再加上增量。
hincrby myhash field1 1
hincrby myhash field1 -1
8. hsetnx <key> <field> <value>: 当且仅当字段不存在时,设置哈希表中指定字段的值。
hsetnx: 只有当指定字段不存在时,才将其值设置为指定值。如果字段已存在,此命令将不起作用。
hsetnx myhash field1 value1
4.3 数据结构
Hash类型对应的数据结构是两种:ziplist(压缩列表),hashtable(哈希表);当field-value长度较短且个数较少时,使用ziplist,否则使用hashtable;
5、Redis常用数据类型-Zset有序集合
5.1 概念
- 有序不重复集合。
- Redis 有序集合 zset 与普通集合 set 非常相似,是一个没有重复元素的字符串集合;
- 不同之处是有序集合的每个成员都关联了一个评分(score),这个评分(score)被用来按照从最低分到最高分的方式排序集合中的成员。集合的成员是唯一的,但是评分可以是重复的;
- 因为元素是有序的,所以也可以很快的根据评分(score)或者次序(position)来获取一个范围的元素;
- 访问有序集合的中间元素也是非常快的,因此能够使用有序集合作为一个没有重复成员的智能列表;
附加:
1. Zset按照元素的分数score来进行排序
- Redis的有序集合(sorted set)是按照元素的分数(score)来进行排序的,而不是按照插入的顺序或者数据的大小来进行排序;
- 每个有序集合的元素都会关联一个分数,这个分数用来进行排序。当多个元素的分数相同时,会根据元素的字典序(lexicographical order)来进行排序;
- 通过使用不同的命令,可以向有序集合中添加元素,获取指定范围内的元素,按照分数或字典序进行排序等操作;
- 需要注意的是,有序集合的元素是唯一的,不允许重复的元素存在。如果多个元素的分数相同,那么它们会按照字典序进行排序;
2. Zset的分数在不指定的情况下都为0,如果是多值插入,那么也是0(然后Zset会默认根据自己的字典来进行数据的排序)
- 默认插入的时候每个value的分数都为0;
5.2 常用命令
1. zadd <key> <score1> <value1> <score2> <value2> ...: 将一个或多个成员元素及其分数值加入到有序集合中。
zadd myzset 1 member1 2 member2
2. zrange <key> <start> <stop> [withscores]: 返回有序集合中,指定索引范围内的元素。
可以使用 WITHSCORES 选项返回元素的分数。
zrange myzset 0 1 withscores
3. zrangebyscore <key> <min> <max> [withscores] [limit <offset> <count>]: 返回有序集合中,分数值介于 min 和 max 之间的所有成员。
可以使用 WITHSCORES 选项返回成员的分数。
可以使用 LIMIT 选项控制返回结果的数量。
zrangebyscore myzset 0 100 withscores limit 0 10
4. zrevrangebyscore <key> <max> <min> [withscores] [limit <offset> <count>]: 返回有序集合中,分数值介于 max 和 min 之间的所有成员,按分数值递减(从大到小)的次序排列。
可以使用 WITHSCORES 选项返回成员的分数。
可以使用 LIMIT 选项控制返回结果的数量。
zrevrangebyscore myzset 100 0 withscores limit 0 10
5. zincrby <key> <increment> <value>: 为有序集合中指定成员的分数加上增量。
zincrby myzset 2 member1
6. zrem <key> <value>: 删除有序集合中指定的成员。
zrem myzset member1
7. zcount <key> <min> <max>: 统计有序集合中,分数值介于 min 和 max 之间的成员数量。
zcount myzset 0 100
8. zrank <key> <value>: 返回有序集合中指定成员的排名(从0开始),按分数值递增(从小到大)的顺序。
zrank myzset member1
5.3 数据结构
SortedSet(zset)是 Redis 提供的一个非常特别的数据结构,一方面它等价于 Java的数据结构 Map<String,Double〉,可以给每一个元素 value 赋予一个权重 score,另一方面它又类似于 TreeSet,内部的元素会按照权重 score 进行排序,可以得到每个元素的名次,还可以通过 score 的范围来获取元素的列表;
zset 底层使用了两个数据结构;
(1) hash:hash的作用就是关联元素 value 和权重 score,保障元素 value 的唯一性,可以通过元素 value 找到相应的 score 值;
(2) 跳跃表:跳跃表的目的在于给元素 value 排序,根据 score 的范围获取元素列表;
5.4 跳表
简介:
有序集合在生活中比较常见,例如根据成绩对学生排名,根据得分对玩家排名等。对于有序集合的底层实现,可以用数组、平衡树、链表等。数组不便元素的插入删除;平衡树或红黑树虽然效率高但结构复杂;链表查询需要遍历所有效率低。Redis采用的是跳跃表。跳跃表效率堪比红黑树,实现远比红黑树简单;
跳表(Skip List)是一种用于有序元素的数据结构,它在插入、删除、查找元素时具有较高的效率,平均时间复杂度为 O(log n)。跳表是一种类似于平衡树的数据结构,但其实现方式更加简单。
跳表的核心思想是通过添加多级索引来加速查找。在跳表中,每个节点都包含一个值以及若干个指向其他节点的指针。这些指针构成了多层级的索引,使得我们可以通过跳过部分节点来快速定位目标节点。顶层的指针组成了一个快速路径,能够迅速定位到较远的节点,而底层的指针则构成了详细路径,用于具体的查找。
简单的说:
跳表是一种数据结构,用于有序元素的存储和快速检索。它的设计类似于链表,但是在链表的基础上增加了多级索引。这些索引使得跳表可以在查找元素时跳过一些不必要的节点,从而提高了查找效率。
举个例子,想象一下有一个存储数字的跳表。这个跳表的每个节点都包含一个数字,并且节点之间有一些指针连接它们。这些指针不是简单地连接相邻节点,而是连接到其他级别的节点,形成了一个多层结构。
当你要查找一个特定的数字时,跳表会从顶层开始检查,并沿着索引向下移动,直到找到目标数字或确定它不存在。由于跳表是有序的,所以可以根据节点的值比较来决定下一步应该向左还是向右移动。
跳表的优点之一是它的查找操作效率很高,因为它可以跳过一些不必要的节点。而且跳表的插入和删除操作也相对简单,不像其他数据结构那样需要频繁地重新平衡。
总的来说,跳表是一种高效的数据结构,特别适用于那些需要快速查找和动态更新的场景。
跳表的结构如下图所示: