Redis基础知识
Redis默认有16个数据库,默认使用的是第0个数据库,可以使用
select
进行切换数据库
Windows下启动redis
C:\Users\lml>redis-cli
127.0.0.1:6379>
# 运行客户端后,可使用以下命令进行测试
select [index] # 切换到第 index 个数据库(0~15)
dbsize # 查看当前数据库存储空间(已经使用的空间)
set [attr] [value]
get [key] # 不同db中的keys是不共享的
keys * # 查看db所有的key
flushdb # 清空当前db
flushall # 清空所有db
exists [key] # 判断某个属性是否存在;存在:1;不存在:0
move [key] [index] # 移动一个属性到第index个库
expire [key] [secends] # 设置一个属性secends秒之后过期 使用单点登录的时候可以用
ttl [key] # 判断key的剩余存活时间 单位是s;如果失效,会返回 -2
type [key] # 查看key的value类型
# 官网上有全部的命令和对应的例子
Redis是单线程的
- Redis是很快的
- Redis是基于内存操作的,CPU并不是Redis的性能瓶颈,Redis的瓶颈是根据机器的内存和网络带宽,既然可以使用单线程来实现,就使用单线程了
- Redis是由C语言编写的,官方提供的数据为100000+的QPS,完全不比同样使用key-value的Memcache差
Redis为什么单线程还这么快?
两个误区
- 高性能的服务器一定是多线程的???
- 多线程(CPU上下文会切换)一定比单线程效率高???
速度:CPU>内存>硬盘
核心:Redis是将所有的数据全部存放在内存中,所以说使用单线程去操作效率是最高的,多线程(CPU上下切换,比较耗时),对于内存系统来说,如果没有上下文切换效率就是最高的,多次读写都是在一个CPU上,在内存情况下,单线程就是最佳方案
五大数据类型
1.String
# group 1:增加,求长 append strlen
set name qd # set [key] [value]
append name 123 # append [key] [append_value] 追加字符串
strlen name # strlen [key] 判断key的字符串长度
append name2 zhangsan # append [key] [value] 如果没有当前key,就相当于set name2 zhangsan
# group 2: 自增 自减 按照步长自增 按照步长自减 incr decr incrby decrby
set views 0 # set [key] [value]设置浏览量为0
incr views # incr [key] 给浏览量加一==》 1
decr views # decr [key] 给浏览量减一==》0
incrby views 10 # incrby [key] [step] 自增步长:10
decrby views 10 # decrby [key] [step] 自减步长:10
# group 3:字符串范围 getrange [startIndex] [endIndex] 一般情况下,两者都大于0,且endIndex>startIndex
getrange name 0 3 # getrange [key] [startIndex] [endIndex] 按照范围取字符,闭区间[startIndex,endIndex]
getrange name 0 -1 #getrange [key] [startIndex] -1 从starIndex取到最后,全部字符
#group 4:替换指定位置开始的字符串 setrange (相当于String中的replace)
setrange name 2 sub # setrange [key] [startIndex] [subString] 使用subString替代从statIndex开始的之后和subString相同长度的字符串;返回的为替代之后的长度
# group 5: setex(作用同expire) setnx
#setex (set with expire) 设置过期时间
#setnex (set if not exit) 不存在则设置(在分布式锁常常使用)
setex name 30 "hello" # setex [key] [expireTime] [value] 设置key存活时长为expireTime,设置值为value (若key不存在则创建)
setnx key1 "qd" # setnx [key] [value] 当key不存在的时候设置值为value;存在会创建失败:(设置成功会返回1,设置失败会返回0)
# group 6:批量设置mset mget msetex msetnx
mset k1 v1 k2 v2 k3 v3 # 批量设置(k1,v1),(k2,v2),(k3,v3) mset [key1] [value1] [key2] [value2] [key3] [value3] ... 存储的顺序是哈希算法设置的
mget k1 k2 # 批量获取 k1 k2... mget [key1] [key2] ...
msetnx k1 v1 k4 v4 # msetnx是一个原子性操作,要么一起成功,要么一起失败
# 进阶 使用对象
set user:1 {name:zhangsan,age:3} # 设置一个user:1对象 值为json字符串来保存一个对象
# 如何获取到单独的属性值????
mset user:1:name name1 user:1:age 2 # 批量设置对象属性 [object]:[id]:[field]
mget user:1:name user:1:age # 批量获取数据
# group 7:getset 先get再set
getset db redis # 返回(nil)。获取的是db之前的值,如果之前没有设置,则返回(nil)。如果重复命令,获取的值为本次命令设置的值(redis)
get db # 本次返回的redis
- String类似的场景:value除了是字符串也可以是数字
- 应用:计数器 统计多单位的数量 粉丝数 对象缓存存储
- 扩展:CAS:比较并交换
- JRedis:java操作的客户端,这些命令都会成为方法
- 结构是相同的
2.List
- 实际上是一个链表。
- 大部分list命令都是由
l
开头:除了lpush和rpush表示左右,其他的l都表示list - 基本命令
# group 1 插入 lpush rpush
lpush list one # 将一个值或者多个值,插入到列表头部(从左端插入:头插)
lpush list two
lpush list three
lrange list 0 -1 # 获取list中的值:输出为 three two one (没有rrange)
lrange list 0 1 # 通过区间获取具体的值
rpush list four # 将一个值或者多个值,插入到列表尾部(从右端插入:尾插)
lrange list 0 -1 # 得到的结果为:three two one four
# group 2 移除lpop rpop
lpop list # 向左移除一个元素,返回的是元素内容:three
rpop list # 向右移除一个元素,返回的是元素内容:four
# group 3 按照索引取值
lindex list 0 # 索引从0开始,-1代表取最后一个元素 没有rindex
# group 4 获取列表长度 llen
llen list # 返回列表长度
# group 5 移除指定值 count>0 移除count个(从前往后); count=0 移除所有; count<0移除count个(从后往前)
lrem list 0 one # 移除list中所有的one
lrem list 1 one # 移除list中的一个one
# group 6 截断操作
lpush list2 one two three four # 新的list
ltrim list2 1 2 # 截断:截断的时候两个参数是index(从0开始),会改变list
# group 7 rpoplpush:移出列表的最后一个元素,并将它放到一个新list中 旧的list可以和新的list是同一个
lpush list3 one two three four # 新的list:list3[four,three,two,one]
rpoplpush list3 list4 # "one"
lrange list3 0 -1 # 得到的结果为: four three two
lrange list4 0 -1 # 得到的结果为:one
# group 8 lset 给一个已经存在的list修改已经存在的index的值:相当于更新操作
lpush list5 one two three four # 新的list:list5[four,three,two,one]
exists list5 # 判断是否存在,返回0(不存在)或 1(存在)
lset list5 0 item # 将list5中的第0个元素设置为“item”,注意index的位置
# group 9 linsert 将某个具体的value插入到列表中某个元素的前面或后面
rpush list6 one one three four # 新的list:list6[one,one,three,four]
linsert list6 before one qd # 将qd插入到one前面:list6[qd,one,one,three,four]
linsert list6 after one qd2 # 将qd2插入到one后面:list6[qd,one,qd2,one,three,four]
# 如果有重复,会按照第一个为标准
- 小结
- 实际上是一个链表,
- 如果key不存在,则创建新的链表
- 如果key存在新增内容
- 如果移除了所有值,即空链表,也代表不存在
- 当在两边插入或者改动值的时候,效率最高;操作中间元素的时候相对效率会低一些
3. Set 集合
- 无序不能重复值
- 基础命令
sadd set0 "one" "two" "three" "four" # set0集合中添加元素
smembers set0 # 查看set0的值
sismember set0 one # 判断set0中是否存在 one 元素
scard set0 #查看set0的元素个数
srem set0 one # 移除set0中的指定元素
srandmember set0 # 随机从set0中取元素,默认为取一个
srandmember set0 2 # 随机从set0中取两个元素
spop set0 # 随机移除set0中的一个元素;返回值为移除的值
spop set0 2 # 随机移除set0中的两个元素;返回值为移除的值
sadd set1 "hello" "123" "qd" # 创建set1
sadd set2 "hello2" "1232" "qd2" # 创建set2
smove set1 set2 "hello" # 将set1中的"hello" 移动到set2
# 差集sdiff 交集sunion 并集sinter
sadd key1 a b c # 创建key1
sadd key2 b c d # 创建key2
sdiff key1 key2 # 求key1与key2的差集
sinter key1 key2 # 求key1与key2的交集
sunion key1 key2 # 求key1与key2的并集
- 应用:微博,A用户将所有关注放在一个set中,将其粉丝放在一个集合中:可以求和其他用户的共同关注,共同爱好,二度好友(六度分割理论),推荐好友(六度分割理论)
4. Hash(哈希)
- 想成一个Map集合:key-value(map):此处的value是map的类型
- 基础命令
# group 1
hset myhash field1 lxl # 创建一个hash:myhash
hmset myhash field1 lxl field2 qd # 可以一次赋值多个(也可以用hset操作)
hget myhash field1 # 获取一个field的值
hmget myhash field1 field2 # 获取多个field的值
hgetall myhash # 获取全部的数据
# group 2
hdel myhash field1 # 删除hash指定的field,也会删除对应的value
hlen myhash # 查看hash中有多少对键值对
hexists myhash field1 # 判断hash中是否有对应的field 返回1或者0
# group 3 只获取key 只获取value
hkeys myhash # 获取所有的field
hvals myhash # 获取所有的value
# group 4 hincrby hsetnx
hset myhash field3 5 # 给myhash设置一个field3 值为5
hincrby myhash field3 2 # 使得myhash中的field3自增2(不可以省略步长)
hincrby myhash field3 -2 # 使得myhash中的field3自减2(不可以省略步长,没有hdecrby方法)
hsetnx myhash field4 hello # 如果不存在,则创建,存在则创建失败
应用:
- hash存储一些变更的数据: name age …,尤其是用户信息之类的经常变动的信息
- hash更实用于对象的存储,String更适合字符串存储
hset user:1 name qd age 20 # 设置id为1的用户的name age hget user:1 name # 获取user:1的name hmset user:1 name age # 获取user:1的name age
5 .Zset(有序集合)
- 在set的基础上增加了一个值:set k1 v1 ----->zset k1 score1 v1
- 有序集合底层数据结构是跳跃链表(常见面试考点)
- 基础命令
# score是可以重复的,元素值不重复 zadd myset 1 one # 创建zset 添加一个值 zadd myset 2 two 3 three # 一次添加多个值 # group 2 排序 zadd salary 2500 xiaohong zadd salary 5000 zhangsan zadd salary 500 qd zrangebyscore salary -inf +inf # 显示所有用户,从小到大排序范围:[-inf,+inf] (前者<后者,不能相等),返回的值只有元素值,不带着score # 使用zrevrangebyscore 可以实现从大到小排列,这时候传的值需要前者大于后者,同样不可以相等 zrangebyscore salary 500 5500 zrangebyscore salary -inf +inf withscores # 带着score # 移除 zrem salary xiaohong # 移除一个元素 xiaohong,同时移除score # 获取指定区间的成员数量 zcard salary # 获取有序集合元素中的个数 zcount salary 500 5500 # 统计薪资在[500,5500]的人数
应用:
- set排序 存储班级成绩表 工资表排序 排行榜
- 带权重进行判断