Redis(一)

270 阅读12分钟

1.Redis介绍

Redis是一种运行速度很快,并发很强,并且运行在内存上的NoSql ( not only sql )数据库

1.1 互联网需求的三高

  • 高并发,高可扩,高性能

1.2 较传统数据库的优势

  • NoSQL数据库无需事先为要存储的数据建立字段,随时可以存储自定义的数据格式。
  • 而在关系数据库里,增删字段是一件非常麻烦的事情。如果是非常大数据量的表,增加字段简直就是一个噩梦

1.3 Redis的常用使用场景

1.3.1 缓存

毫无疑问这是Redis当今最为人熟知的使用场景。在提升服务器性能方面非常有效;一些频繁被访问的数据,经常被访问的数据如果放在关系型数据库,每次查询的开销都会很大,而放在redis中,因为redis是放在内存中的可以很高效的访问。

1.3.2 排行榜

在使用传统的关系型数据库(mysql,oracle等)来做这个事儿,非常的麻烦,而利用Redis的SortSet(有序集合)数据结构能够简单的搞定;

1.3.3 计算器/限速器

利用Redis中原子性的自增操作,我们可以统计类似用户点赞数、用户访问数等,这类操作如果用MySQL,频繁的读写会带来相当大的压力;

限速器比较典型的使用场景是限制某个用户访问某个API的频率,常用的有抢购时,防止用户疯狂点击带来不必要的压力;

1.3.4 好友关系

利用集合的一些命令,比如求交集、并集、差集等。可以方便搞定一些共同好友、共同爱好之类的功能;

1.3.5 简单消息队列

除了Redis自身的发布/可订阅模式,我们也可以利用List来实现一个队列机制,比如:到货通知、邮件发送之类的需求,不需要高可靠,但是会带来非常大的DB压力,完全可以用List来完成异步解耦;

1.3.6 Session共享

以jsp为例,默认Session是保存在服务器的文件中,如果是集群服务,同一个用户过来可能落在不同机器上,这就会导致用户频繁登陆;采用Redis保存Session后,无论用户落在那台机器上都能够获取到对应的Session信息。

2. Redis入门

2.1 Redis安装

2.1.1 上传tar.gz包,并解压

tar -zxvf redis-5.0.4.tar. gz

2.1.2 安装gcc

yum -y insta11 gcc

忘记是否安装过,可以使用gcc -v命令查看gcc版本,如果没有安装过,会提示命令不存在

2.1.3 进入redis目录,进行编译

make

2.1.4 编译之后,开始安装

make insta11

2.2 安装后的操作

2.2.1 后台运行方式

  • redls默认不会使用后台运行,修改配置文件daemonize=yes。
vim /opt/redis-5.0.4/redis.conf
  • 以配置文件的方式启动
cd /usr/local/bin

redis-server /opt/redis-5.0.4/redis.conf
  • usr/local/bin 下的命令
    • redis-server :启动 redis 服务
    • redis-cli :进入 redis 命令客户端
    • redis-benchmark : 性能测试的工具
    • redis-check-aof : aof 文件进行检查的工具
    • redis-check-dump : rdb 文件进行检查的工具
    • redis-sentinel : 启动哨兵监控服务

2.2.2 关闭数据库

  • 单实例关闭
redis-cli shutdown
  • 多实例关闭
redis-cli -p 6379 shutdown

2.2.3 监听端口

netstat -lntp | grep 6379
  • 检测后台进程是否存在
ps -ef | grep redis

2.2.4 性能测试

redis-benchmark
[root@VM-8-15-centos bin]# redis-benchmark
====== PING_INLINE ======
  100000 requests completed in 1.32 seconds # 1.32秒处理了10万个请求
  50 parallel clients
  3 bytes payload
  keep alive: 1

99.55% <= 1 milliseconds
99.82% <= 2 milliseconds
99.89% <= 3 milliseconds
99.90% <= 4 milliseconds
99.94% <= 5 milliseconds
99.95% <= 6 milliseconds
100.00% <= 6 milliseconds
75585.79 requests per second # 每秒处理的请求数量 

====== PING_BULK ======

2.2.5 默认16个数据库

vim /usr/local/redis-5.0.13/redis.conf

databases 16

  • 切换数据库
127.0.0.1:6379> select 16 # 切换16号数据库
(error) ERR DB index is out of range # 越界

127.0.0.1:6379> select 15
OK

2.2.6 查询当前数据库键的数量

dbsize

2.2.7 模糊查询key

(1) * :通配任意多个字符

  • 查询所有的key
keys *
  • 模糊查询n开头,后面随便多少个字符
keys n*
  • 模糊查询e为最后一位,前面随便多少个字符
keys *e
  • 双*模式,匹配任意多个字符:查询包含k的键
keys *k*

(2) ? :通配单个字符

  • 模糊查询k字头,并且匹配一个字符
keys k?
  • 你只记得第一个字母是k,他的长度是3
keys k??

(3) []:通配括号内的某一个字符

  • 记得其他字母,就第二个字母可能是a或e
keys r[ae]dis

2.2.8 key的其它操作

  • exists key :判断某个key是否存在
127.0.0.1:6379> exists k1

(integer) 1 #存在

127.0.0.1:6379> exists y1

(integer) 0 #不存在
  • move key db :移动(剪切,粘贴)键到几号库
127.0.0.1:6379> move x1 8 #将x1移动到8号库
(integer) 1 #移动成功

127.0.0.1:6379>exists x1 #查看当前库中是否存在x1
(integer) 0 #不存在(因为已经移走了)

127.0.0.1:6379> select 8 #切换8号库
oK

127.0.0.1:6379[8]> keys * #查看当前库中的所有键

1) "x1"

  • expire key 秒:为键设置过期时间
expire k1 60 
  • ttl key:查看键还有多久过期( -1永不过期,-2已过期)

  • type key:查看键的数据类型

2.2.9 删除key

flushdb #删除当前数据库中的所有key 

flushall #删除所有数据库中的key 

3. 使用Redis

3.1 五大数据类型

3.1.1 字符串String

(1) set/get/del/append/strlen

127.0.0.1:6379> set k1 v1 #保存数据
OK

127.0.0.1:6379> set k2 v2 #保存数据
OK

127.0.0.1:6379> keys *
1) "k1"
2) "k2"

127.0.0.1:6379> del k2 #删除数据k2
(integer) 1

127.0.0.1:6379> get k1 #获取数据k1
"v1"

127.0.0.1:6379> append k1 abc #往k1的值追加数据abc
(integer) 5

127.0.0.1:6379> get k1
"v1abc"

127.0.0.1:6379> strlen k1 #返回k1值的长度(字符数量)
(integer) 5

(2) incr/decr/incrby/decrby

加减操作,操作的必须是数字类型

127.0.0.1:6379> set k1 1 #初始化k1的值为1
OK

127.0.0.1:6379> incr k1 #k1自增1(相当于++)
(integer) 2

127.0.0.1:6379> incr k1
(integer) 3

127.0.0.1:6379> get k1
"3"

127.0.0.1:6379> decr k1 #k1自减1《相当于--)
(integer) 2

127.0.0.1:6379> decr k1
(integer) 1

127.0.0.1:6379> get k1
"1"

127.0.0.1:6379> incrby k1 3 #k1自增3〈相当于+=3)
(integer) 4

127.0.0.1:6379> get k1
"4"

127.0.0.1:6379> decrby k1 2  #相当于-=2
(integer) 2

127.0.0.1:6379> get k1
"2"

(3) getrange/setrange

类似 between...and...

127.0.0.1:6379> set k1 abcdef #初始化k1的值为abcdef
OK

127.0.0.1:6379> get k1 
"abcdef"

127.0.0.1:6379> getrange k1 0 -1 # 查询k1全部值
"abcdef"

127.0.0.1:6379> getrange k1 0 3 # 查询k1下标0-3 
"abcd"

127.0.0.1:6379> setrange k1 1 xxx # 从下标1开始替换 
(integer) 6

127.0.0.1:6379> get k1
"axxxef"

(4) setex/setnx

  • set with expir:添加数据的同时设置生命周期
127.0.0.1:6379> setex k1 10 v1 #添加k1 v1数据的同时,设置10秒的生命周期
OK

127.0.0.1:6379> get k1
"v1"

127.0.0.1:6379> get k1 # #已过期
(nil)
  • set if not exist:添加数据的时候判断是否已经存在,防止已存在的数据被覆盖掉
127.0.0.1:6379> setnx k1 v1
(integer) 0 #添加失败,因为k1已经存在

127.0.0.1:6379> setnx k2 v2
(integer) 1 #添加成功
 

(5) mset/mget/msetnx

  • m:more
127.0.0.1:6379> set k1 v1 k2 v2 # set不支持一次添加多条数据
(error) ERR syntax error

127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3
OK

127.0.0.1:6379> keys *
1) "k2"
2) "k1"
3) "k3"

127.0.0.1:6379> mget k1 k2 k3 #一次获取多条数据
1) "v1"
2) "v2"
3) "v3"

127.0.0.1:6379> msetnx k3 v3 k4 v4 #一次添加多条数据时,如果添加的数据中有已经存在的,则失败
(integer) 0

127.0.0.1:6379> msetnx k4 v4 k5 v5 #一次添加多条数据时,如果添加的数据中都不存在的,则成功
(integer) 1

127.0.0.1:6379> keys *
1) "k1"
2) "k3"
3) "k5"
4) "k2"
5) "k4"

(6) getset

  • 先get后set
127.0.0.1:6379> getset k1 v1
(nil)
127.0.0.1:6379> get k1
"v1"
127.0.0.1:6379> getset k1 v11
"v1"
127.0.0.1:6379> get k1
"v11"

3.1.2 列表List

(1)lpush/rpush/lrange

  • 添加元素
127.0.0.1:6379> lpush list1 0 1 2 3 4 5 #从上往下添加
(integer) 6

127.0.0.1:6379> lrange list1 0 -1 #查询1ist01中的全部数据O表示开始,-1表示结尾
1) "5"
2) "4"
3) "3"
4) "2"
5) "1"
6) "0"

127.0.0.1:6379> rpush list2 0 1 2 3 4 5 #从下往上添加
(integer) 6

127.0.0.1:6379> lrange list2 0 -1
1) "0"
2) "1"
3) "2"
4) "3"
5) "4"
6) "5"

(2) lpop/rpop

  • 移除第一个元素
127.0.0.1:6379> lpop list2 #从左(上)边移除第一个元素
"0"

127.0.0.1:6379> rpop list2 #从右(下)边移除第一个元素
"5"

(3)lindex

  • 根据下标查询元素(从左向右,自上而下)
127.0.0.1:6379> lrange list1 0 -1
1) "5"
2) "4"
3) "3"
4) "2"
5) "1"
6) "0"

127.0.0.1:6379> lindex list1 0
"5"

127.0.0.1:6379> lindex list1 5
"0"

(4)llen

  • 返回集合长度
127.0.0.1:6379> llen list1
(integer) 6

(5)lrem

  • 删除n个元素
127.0.0.1:6379> lrem list1 0 2
(integer) 1

127.0.0.1:6379> lrange list1 0 -1
1) "5"
2) "4"
3) "3"
4) "1"
5) "0"

(5)ltrim

  • 截取指定范围的值,别的全扔掉
  • ltrim key begindex endindex
127.0.0.1:6379> lrange list2 0 -1
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"

127.0.0.1:6379> ltrim list2 0 3
OK

127.0.0.1:6379> lrange list2 0 -1
1) "1"
2) "2"
3) "3"
4) "4"

(5)rpoplpush

  • 从一个集合搞一个元素到另一个集合中(右出一个,左进一个)
127.0.0.1:6379> lrange list1 0 -1
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"

127.0.0.1:6379> lrange list2 0 -1
1) "6"
2) "7"
3) "8"
4) "9"

127.0.0.1:6379> rpoplpush list1 list2
"5"

127.0.0.1:6379> lrange list1 0 -1
1) "1"
2) "2"
3) "3"
4) "4"

127.0.0.1:6379> lrange list2 0 -1
1) "5"
2) "6"
3) "7"
4) "8"
5) "9"

(6)lset

  • 改变某个下标的某个值
  • lset key index value
127.0.0.1:6379> lrange list1 0 -1
1) "1"
2) "2"
3) "3"
4) "4"

127.0.0.1:6379> lset list1 0 x
OK

127.0.0.1:6379> lrange list1 0 -1
1) "x"
2) "2"
3) "3"
4) "4"

(6)linsert

  • 插入元素(指定某个元素之前/之后)
  • linsert key before/after oldvalue newvalue
127.0.0.1:6379> lrange list1 0 -1
1) "x"
2) "2"
3) "3"
4) "4"

127.0.0.1:6379> linsert list1 after x java
(integer) 5

127.0.0.1:6379> lrange list1 0 -1
1) "x"
2) "java"
3) "2"
4) "3"
5) "4"

3.1.3 集合Set

  • 和java中的set特点类似,不允许重复

(1)sadd/smembers/sismember

  • 添加/查看/判断是否存在
127.0.0.1:6379> sadd set1  1 2 2 3 3 4 4 5  #添加元素(自动排除重复)
(integer) 5

127.0.0.1:6379> smembers set1 #查询set1集合
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"

127.0.0.1:6379> sismember set1 5
(integer) 1 # 存在

127.0.0.1:6379> sismember set1 6
(integer) 0 # 不存在

#注意:1和0可不是下标,而是布尔。
# 1 : true存在,2 : false不存在

(2)scard

  • 获取集合中的元素个数
127.0.0.1:6379> smembers set1
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"

127.0.0.1:6379> scard set1
(integer) 5

(3)srem

  • 删除集合中的元素
  • srem key value
127.0.0.1:6379> smembers set1
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"

127.0.0.1:6379> srem set1 1
(integer) 1

127.0.0.1:6379> smembers set1
1) "2"
2) "3"
3) "4"
4) "5"

(4)srandmember

  • 从集合中随机获取几个元素
127.0.0.1:6379> smembers set1
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
6) "6"
7) "7"
8) "8"
9) "9"

127.0.0.1:6379> srandmember set1 3
1) "3"
2) "7"
3) "1"

127.0.0.1:6379> srandmember set1 3
1) "4"
2) "7"
3) "1"

127.0.0.1:6379> srandmember set1 3
1) "7"
2) "1"
3) "5"

(5)spop

  • 随机出栈(移除)
127.0.0.1:6379> smembers set1
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
6) "6"
7) "7"
8) "8"
9) "9"

127.0.0.1:6379> spop set1 #随机移除一个元素
"5"

127.0.0.1:6379> spop set1
"8"

127.0.0.1:6379> spop set1
"1"

127.0.0.1:6379> smembers set1
1) "2"
2) "3"
3) "4"
4) "6"
5) "7"
6) "9"

(5)数学集合类

  • 交集:sinter
  • 并集:sunion
  • 差集: sdiff
127.0.0.1:6379> smembers set1
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"

127.0.0.1:6379> smembers set2
1) "a"
2) "b"
3) "1"
4) "2"
5) "3"

127.0.0.1:6379> sinter set1 set2 # 交集 set1和set2共同存在的元素
1) "1"
2) "2"
3) "3"

127.0.0.1:6379> sunion set1 set2 # 并集 将set1和set2中所有元素合并起来(排除重复的)
1) "4"
2) "b"
3) "1"
4) "2"
5) "a"
6) "3"
7) "5"

127.0.0.1:6379> sdiff set1 set2 #在set1中存在,在set2中不存在
1) "4"
2) "5"

127.0.0.1:6379> sdiff set2 set1 #在set2中存在,在set1中不存在
1) "a"
2) "b"

3.1.4 哈希Hash

  • 类似java里面的Map<String,Object> KV模式不变,但V是一个键值对

(1)hset/hget/hmset/hmget/ngetall/hdel

  • 添加/得到/多添加/多得到/得到全部/删除属性
127.0.0.1:6379> hset user id 1001 #添加user,值为id=1001
(integer) 1

127.0.0.1:6379> hget user 
(error) ERR wrong number of arguments for 'hget' command

127.0.0.1:6379> hget user id #查询user,必须指明具体的字段
"1001"

127.0.0.1:6379> hmset student id 101 name tom age 22  #添加学生student,属性—堆
OK

127.0.0.1:6379> hget student
(error) ERR wrong number of arguments for 'hget' command 必须指明具体的字段

127.0.0.1:6379> hget student name #获取学生名字
"tom"

127.0.0.1:6379> hget student name age  #获取多属性 用hmget
(error) ERR wrong number of arguments for 'hget' command

127.0.0.1:6379> hmget student name age #获取学生姓名和年龄
1) "tom"
2) "22"

127.0.0.1:6379> hgetall student #获取学生全部信息
1) "id"
2) "101"
3) "name"
4) "tom"
5) "age"
6) "22"

127.0.0.1:6379> hdel student age #删除学生年龄属性
(integer) 1

127.0.0.1:6379> hgetall student
1) "id"
2) "101"
3) "name"
4) "tom"

(2)hlen

  • 返回元素的属性个数
127.0.0.1:6379> hgetall student
1) "id"
2) "101"
3) "name"
4) "tom"

127.0.0.1:6379> hlen student 
(integer) 2 # student属性的数量,id和name,共两个属性

(3)hexists

  • 判断元素是否存在某个属性
127.0.0.1:6379> hgetall student
1) "id"
2) "101"
3) "name"
4) "tom"

127.0.0.1:6379> hexists student name 
(integer) 1 # 存在
 
127.0.0.1:6379> hexists student age
(integer) 0 #不存在

(4)hkeys/hvals

  • 获得属性的所有key/获得属性的所有value
127.0.0.1:6379> hgetall student
1) "id"
2) "101"
3) "name"
4) "tom"

127.0.0.1:6379> hkeys student
1) "id"
2) "name"

127.0.0.1:6379> hvals student
1) "101"
2) "tom"

(5)hincrby/hincrbyfloat

  • 自增(整数)/自增(小数)
127.0.0.1:6379> hgetall student
1) "id"
2) "101"
3) "name"
4) "tom"
5) "age"
6) "18"

127.0.0.1:6379> hincrby student age 1
(integer) 19

127.0.0.1:6379> hgetall student
1) "id"
2) "101"
3) "name"
4) "tom"
5) "age"
6) "19"

(6)hsetnx

  • 添加的时候,先判断是否存在
127.0.0.1:6379> hsetnx student age 20
(integer) 0

127.0.0.1:6379> hgetall student
1) "id"
2) "101"
3) "name"
4) "tom"
5) "age"
6) "18"

127.0.0.1:6379> hsetnx student sex 1
(integer) 1

127.0.0.1:6379> hgetall student
1) "id"
2) "101"
3) "name"
4) "tom"
5) "age"
6) "18"
7) "sex"
8) "1"

3.1.5 有序集合Zset

  • 真实需求∶
    • 充10元可享vip1;
    • 充20元可享vip2;
    • 充30元可享vip3;
    • 以此类推...

(1)zadd/zrange ( withscores )

  • 添加/查询
127.0.0.1:6379> zadd zset1 10 vip1 20 vip2 30 vip3 40 vip4 50 vip5
(integer) 5

127.0.0.1:6379> zrange zset1 0 -1 #查询数据
1) "vip1"
2) "vip2"
3) "vip3"
4) "vip4"
5) "vip5"

127.0.0.1:6379> zrange zset1 0 -1 withscores #带着分数查询数据
 1) "vip1"
 2) "10"
 3) "vip2"
 4) "20"
 5) "vip3"
 6) "30"
 7) "vip4"
 8) "40"
 9) "vip5"
10) "50"

(2)zrangebyscore

  • 模糊查询
    • (:不包含
    • limit :跳过几个截取几个
127.0.0.1:6379> zrangebyscore zset1 20 40  # 20 <= score <= 40
1) "vip2"
2) "vip3"
3) "vip4"

127.0.0.1:6379> zrangebyscore zset1 20 (40 # 20 <= score < 40
1) "vip2"
2) "vip3"

127.0.0.1:6379> zrangebyscore zset1 (20 (40 # 20 < score <40
1) "vip3"

127.0.0.1:6379> zrangebyscore zset1 10 40  
1) "vip1"
2) "vip2"
3) "vip3"
4) "vip4"

127.0.0.1:6379> zrangebyscore zset1 10 40 limit 1 2 # 10 <= score <= 40,共返回四个,跳过第1个,取2个
1) "vip2"
2) "vip3"

(3)zrem

  • 删除元素
127.0.0.1:6379> zr em zset01 vip5 #移除vip5
(integer) 1

(4)zcard/zcount/zrank/zscore

  • 集合长度/范围内元素个数/得元素下标/通过值得分数
127.0.0.1:6379> zrange zset1 0 -1
1) "vip1"
2) "vip2"
3) "vip3"
4) "vip4"
5) "vip5"

127.0.0.1:6379> zcard zset1 #集合中元素的个数
(integer) 5

127.0.0.1:6379> zcount zset1 20 30 #分数在20~30之间,共有几个元素
(integer) 2

127.0.0.1:6379> zrank zset1 vip3 # vip3在集合中的下标(从上向下)
(integer) 2

127.0.0.1:6379> zscore zset1 vip2 #通过元素获得对应的分数
"20"

(5)zrevrange

  • 逆序查询
127.0.0.1:6379> zrange zset1 0 -1 # 顺序查询
1) "vip1"
2) "vip2"
3) "vip3"
4) "vip4"
5) "vip5"

127.0.0.1:6379> zrevrange zset1 0 -1 # 逆序查询
1) "vip5"
2) "vip4"
3) "vip3"
4) "vip2"
5) "vip1"

(6)zrevrangebyscore

  • 逆序范围查找
127.0.0.1:6379> zrevrangebyscore zset1 30 20 #逆序查询分数在30~20之间的(注意,先写大值,再写小值)
1) "vip3"
2) "vip2"

127.0.0.1:6379> zrevrangebyscore zset1 20 30 #如果小值在前则结果为nu11
(empty list or set)