redis概述

redis是什么?

REmote DIctionary Server(Redis) 
Redis 是一个高性能的key-value数据库
性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。

windows安装

下载地址:https://github.com/tporadowski/redis/releases

image-20221230232353283

linux安装

下载地址:http://redis.io/download

image-20221231214947545

五大数据类型

redis-key

select 0(0-15)  //切换第0个数据库
dbsize //查看当前数据库大小
flushdb //清除当前数据库
flushall //清除所有数据库
type name //判断key 类型
keys * //查看所有的key
set name miaomiao //set key
get name //get key
del name //删除key name
exists //判断当前key是否存在
move name 1 //移除key 在一号数据库
expire name 10 //设置key 10秒后过期
ttl name //查看当前key剩余时间
type name //查看当前key类型

string

127.0.0.1:6379> set key1 v1
OK
127.0.0.1:6379> keys *
1) "age"
2) "name"
3) "key1"
127.0.0.1:6379> exists key1 //查看key1是否存在
(integer) 1
127.0.0.1:6379> append key1 'hello' //为key1追加内容
(integer) 7
127.0.0.1:6379> exists key1
(integer) 1
127.0.0.1:6379> get key1
"v1hello"
127.0.0.1:6379> append key1 ',miaostudy'
(integer) 17
127.0.0.1:6379> get key1
"v1hello,miaostudy"
127.0.0.1:6379> strlen key1 //查看key1字符长度
(integer) 17
127.0.0.1:6379>
###################################
127.0.0.1:6379> keys *
1) "age"
2) "name"
3) "key1"
127.0.0.1:6379> append key2 'aaabbb' //如果追加key不存在,就相当于set key
(integer) 6
127.0.0.1:6379> keys *
1) "age"
2) "name"
3) "key1"
4) "key2"
127.0.0.1:6379>
#############################################
127.0.0.1:6379> set views 0
OK
127.0.0.1:6379> get views
"0"
127.0.0.1:6379> incr views //加一
(integer) 1
127.0.0.1:6379> incr views
(integer) 2
127.0.0.1:6379> decr views //减一
(integer) 1
127.0.0.1:6379> decr views
(integer) 0
127.0.0.1:6379> incrby views 9 //设置一次性加9
(integer) 9
127.0.0.1:6379> incrby views 9
(integer) 18
127.0.0.1:6379> decrby views 5 //设置一次性减5
(integer) 13
127.0.0.1:6379> decrby views 5
(integer) 8
127.0.0.1:6379>
############################################
字符串范围 range
127.0.0.1:6379> set info hfaksdhsdkjh
OK
127.0.0.1:6379> get ingo
(nil)
127.0.0.1:6379> get info
"hfaksdhsdkjh"
127.0.0.1:6379> getrange info 0 3 //获取范围
"hfak"
127.0.0.1:6379> getrange info 0 -1 //-1 查看长度
"hfaksdhsdkjh"
127.0.0.1:6379>
############################################
setrange 替换
127.0.0.1:6379[2]> set key1 adcdeeee
OK
127.0.0.1:6379[2]> get key1
"adcdeeee"
127.0.0.1:6379[2]> setrange key1 1 xx //d 替换 xx
(integer) 8
127.0.0.1:6379[2]> get key1
"axxdeeee"
127.0.0.1:6379[2]>
#######################################
setex(set with expire)设置过期时间
setnx(set if not exist)不存在设置
127.0.0.1:6379[2]> setex key3 30 'mymy'//设置key过期时间
OK
127.0.0.1:6379[2]> ttl key3 //查看剩余时间
(integer) 26
127.0.0.1:6379[2]> ttl key3
(integer) 14
127.0.0.1:6379[2]> get key3
(nil)
127.0.0.1:6379[2]> setnx mykey 'wodekey' //如果不存在设置key
(integer) 1
127.0.0.1:6379[2]> get mykey
"wodekey"
127.0.0.1:6379[2]> setnx mykey 'nidekey'
(integer) 0
127.0.0.1:6379[2]> get mykey
"wodekey"
127.0.0.1:6379[2]>
##########################################
mset 一次设置多个值
mget 一次获取多个值
127.0.0.1:6379[2]> mset k1 v1 k2 v2//设置多个值
OK
127.0.0.1:6379[2]> keys *
1) "k2"
2) "k1"
127.0.0.1:6379[2]> mget k1 k2 //获取多个值
1) "v1"
2) "v2"
127.0.0.1:6379[2]> msetnx k1 v1 k3 v3 //msetnx是一个原子性操作,要么一起成功,要么一起失败
(integer) 0
127.0.0.1:6379[2]> keys *
1) "k2"
2) "k1"
127.0.0.1:6379[2]>
################################################
set user:1 {name:mm,age:22} 设置一个user:1 对象,值为json字符保存的对象
127.0.0.1:6379[2]> mset user:1:name lisi user:1:age 22
OK
127.0.0.1:6379[2]> mget user:1:name user:1:age
1) "lisi"
2) "22"
127.0.0.1:6379[2]>
################################################
getset 先get然后set
127.0.0.1:6379[2]> getset db redis //如果存在值 返回并设置新的值
(nil)
127.0.0.1:6379[2]> get db
"redis"
127.0.0.1:6379[2]> getset db mogodb
"redis"
127.0.0.1:6379[2]> get db
"mogodb"
127.0.0.1:6379[2]>

list

所有的list命令都是用l 开头的

127.0.0.1:6379[2]> lpush list one //push元素进去头部
(integer) 1
127.0.0.1:6379[2]> lpush list two
(integer) 2
127.0.0.1:6379[2]> lpush list three
(integer) 3
127.0.0.1:6379[2]> lrange list 0 -1 //获取所有元素
1) "three"
2) "two"
3) "one"
127.0.0.1:6379[2]> lrange list 0 1 //获取范围元素
1) "three"
2) "two"
127.0.0.1:6379[2]>
127.0.0.1:6379[2]> lpush list left //添加元素位置在左边
(integer) 4
127.0.0.1:6379[2]> lrange list 0 -1 //获取list值
1) "left"
2) "three"
3) "two"
4) "one"
127.0.0.1:6379[2]> rpush list right //rpush 添加元素在右边
(integer) 5
127.0.0.1:6379[2]> lrange list 0 -1 //获取区间值
1) "left"
2) "three"
3) "two"
4) "one"
5) "right"
127.0.0.1:6379[2]>
#############################################
lpop 弹出左边值
rpop 弹出右边值
127.0.0.1:6379[2]> lrange list 0 -1
1) "left"
2) "three"
3) "two"
4) "one"
5) "right"
127.0.0.1:6379[2]> lpop list //弹出左边值
"left"
127.0.0.1:6379[2]> rpop list //弹出右边值
"right"
127.0.0.1:6379[2]> lrange list 0 -1
1) "three"
2) "two"
3) "one"
127.0.0.1:6379[2]>
###########################################
lindex 通过下标获取值
127.0.0.1:6379[2]> lrange list 0 -1
1) "three"
2) "two"
3) "one"
127.0.0.1:6379[2]> lindex list 1 //获取下标为1的值
"two"
127.0.0.1:6379[2]> lindex list 0
"three"
127.0.0.1:6379[2]>
###############################################
llen 获取列表长度
127.0.0.1:6379[2]> lrange list 0 -1
1) "three"
2) "two"
3) "one"
127.0.0.1:6379[2]> llen list
(integer) 3
127.0.0.1:6379[2]>
#############################################
lrem 移除指定的值
127.0.0.1:6379[2]> lrange list 0 -1
1) "three"
2) "three"
3) "two"
4) "one"
127.0.0.1:6379[2]> lrem list 1 two //移除一个值为two
(integer) 1
127.0.0.1:6379[2]> lrange list 0 -1
1) "three"
2) "three"
3) "one"
127.0.0.1:6379[2]> lrem list 2 three //移除两个值为three
(integer) 2
127.0.0.1:6379[2]> lrange list 0 -1
1) "one"
127.0.0.1:6379[2]>
#########################################
ltrim 截断list
127.0.0.1:6379[2]> rpush mylist hh1
(integer) 1
127.0.0.1:6379[2]> rpush mylist hh2
(integer) 2
127.0.0.1:6379[2]> rpush mylist hh3
(integer) 3
127.0.0.1:6379[2]> rpush mylist hh4
(integer) 4
127.0.0.1:6379[2]> lrange mylist 0 -1
1) "hh1"
2) "hh2"
3) "hh3"
4) "hh4"
127.0.0.1:6379[2]> ltrim mylist 1 2 //截取下标1 2的值
OK
127.0.0.1:6379[2]> lrange mylist 0 -1
1) "hh2"
2) "hh3"
127.0.0.1:6379[2]>
###############################################
rpoplpush 移除列表最后一个值到另一个列表中
127.0.0.1:6379[2]> lrange mylist 0 -1
1) "hh2"
2) "hh3"
127.0.0.1:6379[2]> rpoplpush mylist myotherlist//移动最后值到新的列表
"hh3"
127.0.0.1:6379[2]> lrange mylist 0 -1
1) "hh2"
127.0.0.1:6379[2]> lrange myotherlist 0 -1 //查看新的列表中的值
1) "hh3"
127.0.0.1:6379[2]>
##########################################
lset 将列表中指定下标的值替换另一个值
127.0.0.1:6379[2]> exists list
(integer) 0
127.0.0.1:6379[2]> lpush list value
(integer) 1
127.0.0.1:6379[2]> lrange list 0 0
1) "value"
127.0.0.1:6379[2]> lset list 0 item //替换下标为0的值
OK
127.0.0.1:6379[2]> lrange list 0 0
1) "item"
127.0.0.1:6379[2]>
###############################################
linsert 将某个元素value插入列表中某个元素的前面或者后面
127.0.0.1:6379[2]> rpush mylist hello
(integer) 1
127.0.0.1:6379[2]> rpush mylist world
(integer) 2
127.0.0.1:6379[2]> lrange mylist 0 -1
1) "hello"
2) "world"
127.0.0.1:6379[2]> linsert mylist after world welcome //world后面插入值welcome
(integer) 3
127.0.0.1:6379[2]> lrange mylist 0 -1
1) "hello"
2) "world"
3) "welcome"
127.0.0.1:6379[2]> linsert mylist before world new //world前面插入值new
(integer) 4
127.0.0.1:6379[2]> lrange mylist 0 -1
1) "hello"
2) "new"
3) "world"
4) "welcome"
127.0.0.1:6379[2]>

set

set的值是不能重复的

127.0.0.1:6379[2]> sadd myset hello //添加元素
(integer) 1
127.0.0.1:6379[2]> sadd myset world
(integer) 1
127.0.0.1:6379[2]> sadd myset welcome
(integer) 1
127.0.0.1:6379[2]> smembers myset //查看所有元素
1) "welcome"
2) "world"
3) "hello"
127.0.0.1:6379[2]> sismember myset hello //判断是否存在某个元素
(integer) 1
127.0.0.1:6379[2]> sismember myset nihao
(integer) 0
127.0.0.1:6379[2]>
##############################################
127.0.0.1:6379[2]> smembers myset
1) "welcome"
2) "world"
3) "hello"
127.0.0.1:6379[2]> scard myset //获取集合元素的个数
(integer) 3
127.0.0.1:6379[2]>
##################################################
127.0.0.1:6379[2]> SMEMBERS myset
1) "welcome"
2) "world"
3) "hello"
127.0.0.1:6379[2]> srem myset hello //移除某一个元素
(integer) 1
127.0.0.1:6379[2]> SMEMBERS myset
1) "welcome"
2) "world"
127.0.0.1:6379[2]>
#################################################
127.0.0.1:6379[2]> SMEMBERS myset
1) "key1"
2) "welcome"
3) "world"
4) "key2"
127.0.0.1:6379[2]> srandmember myset //随机抽取一个元素
"world"
127.0.0.1:6379[2]> srandmember myset
"welcome"
127.0.0.1:6379[2]>
#################################################
127.0.0.1:6379[2]> smembers myset
1) "key1"
2) "welcome"
3) "world"
4) "key2"
127.0.0.1:6379[2]> spop myset //随机弹出一个元素
"key1"
127.0.0.1:6379[2]> spop myset
"key2"
127.0.0.1:6379[2]>
#################################################
127.0.0.1:6379[2]> smembers myset
1) "welcome"
2) "world"
3) "hello"
127.0.0.1:6379[2]> sadd myset2 key1
(integer) 1
127.0.0.1:6379[2]> smove myset myset2 hello //移动元素到另一个集合中
(integer) 1
127.0.0.1:6379[2]> smembers myset2
1) "key1"
2) "hello"
127.0.0.1:6379[2]>
##############################################
127.0.0.1:6379[2]> sadd key a
(integer) 1
127.0.0.1:6379[2]> sadd key b
(integer) 1
127.0.0.1:6379[2]> sadd key c
(integer) 1
127.0.0.1:6379[2]> sadd key1 c
(integer) 1
127.0.0.1:6379[2]> sadd key1 d
(integer) 1
127.0.0.1:6379[2]> sdiff key key1 //两个集合的差集
1) "a"
2) "b"
127.0.0.1:6379[2]> sinter key key1 //两个集合的交集
1) "c"
127.0.0.1:6379[2]> sunion key key1 //两个集合的并集
1) "a"
2) "b"
3) "c"
4) "d"
127.0.0.1:6379[2]>

hash

map集合,key-map,这时候值是一个集合

127.0.0.1:6379[2]> hset myhash field miaostudy //存值
(integer) 1
127.0.0.1:6379[2]> hget myhash field //取值
"miaostudy"
127.0.0.1:6379[2]> hmset myhash field hello field1 world //存入多个值
OK
127.0.0.1:6379[2]> hmget myhash field field1 //取出多个值
1) "hello"
2) "world"
127.0.0.1:6379[2]> hgetall myhash //查看所有值
1) "field"
2) "hello"
3) "field1"
4) "world"
127.0.0.1:6379[2]> hdel myhash field //删除一个字段
(integer) 1
127.0.0.1:6379[2]> hgetall myhash
1) "field1"
2) "world"
127.0.0.1:6379[2]>
#############################################
hlen 获取字符长度
127.0.0.1:6379[2]> hgetall myhash
1) "field1"
2) "world"
3) "field"
4) "hello"
127.0.0.1:6379[2]> hlen myhash //获取hash字段数量
(integer) 2
127.0.0.1:6379[2]>
##############################################
127.0.0.1:6379[2]> hexists myhash field //判断field是否存在,1存在,0不存在
(integer) 1
127.0.0.1:6379[2]> hexists myhash field3
(integer) 0
127.0.0.1:6379[2]> hgetall myhash
1) "field1"
2) "world"
3) "field"
4) "hello"
127.0.0.1:6379[2]>
##############################################
获取所有的field
获取所有的value
127.0.0.1:6379[2]> hkeys myhash //获取所有的field
1) "field1"
2) "field"
127.0.0.1:6379[2]> hvals myhash //获取所有的value
1) "world"
2) "hello"
127.0.0.1:6379[2]> hgetall myhash
1) "field1"
2) "world"
3) "field"
4) "hello"
127.0.0.1:6379[2]>

zset(有序集合)

127.0.0.1:6379[2]> zadd myset 1 one //添加一个值
(integer) 1
127.0.0.1:6379[2]> zadd myset 2 two 3 three //添加多个值
(integer) 2
127.0.0.1:6379[2]> zrange myset 0 -1
1) "one"
2) "two"
3) "three"
127.0.0.1:6379[2]>
##########################################
127.0.0.1:6379[2]> zadd salary 2000 dage
(integer) 1
127.0.0.1:6379[2]> zadd salary 2500 erdi
(integer) 1
127.0.0.1:6379[2]> zadd salary 250 xiaosan
(integer) 1
127.0.0.1:6379[2]> ZRANGEBYSCORE salary -inf +inf //小--大 排序
1) "xiaosan"
2) "dage"
3) "erdi"
127.0.0.1:6379[2]> ZRANGEBYSCORE salary -inf +inf withscores //带条件排序
1) "xiaosan"
2) "250"
3) "dage"
4) "2000"
5) "erdi"
6) "2500"
127.0.0.1:6379[2]> ZRANGEBYSCORE salary -inf +inf withscores
1) "xiaosan"
2) "250"
3) "erdi"
4) "2500"
127.0.0.1:6379[2]> ZREVRANGE salary 0 -1 //大到小排序
1) "erdi"
2) "xiaosan"
127.0.0.1:6379[2]>
##########################################
127.0.0.1:6379[2]> ZRANGEBYSCORE salary -inf +inf withscores
1) "xiaosan"
2) "250"
3) "dage"
4) "2000"
5) "erdi"
6) "2500"
127.0.0.1:6379[2]> zrem salary dage //移除元素
(integer) 1
127.0.0.1:6379[2]> zrange salary 0 -1
1) "xiaosan"
2) "erdi"
127.0.0.1:6379[2]>
#############################################

三种特殊数据类型

geospatial(地理空间)


redis的基本事务操作

redis事务本质:一组命令的集合,一个事务中的所有命令都会被序列化,事务执行过程中,会按照顺序执行。

一次性、顺序性、排他性

redis事务没有隔离级别的概念

所有的命令在事务中,并没有直接执行,只有发起命令的时候才会执行,exec

redis 单条命令保证原子性,但是事务不保证原子性

redis的事务:

  1. 开启事务(multi)
  2. 命令入队(…)
  3. 执行事务(exec)
正常执行事务
127.0.0.1:6379[2]> multi //开启事务
OK
127.0.0.1:6379[2](TX)> set k1 v1 //设置元素入队
QUEUED
127.0.0.1:6379[2](TX)> set k2 v2
QUEUED
127.0.0.1:6379[2](TX)> get k2
QUEUED
127.0.0.1:6379[2](TX)> set k3 v3
QUEUED
127.0.0.1:6379[2](TX)> exec //执行事务
1) OK
2) OK
3) "v2"
4) OK
127.0.0.1:6379[2]>
##############################################
放弃事务
127.0.0.1:6379[2]> keys *
1) "k4"
2) "k2"
3) "k1"
4) "k3"
127.0.0.1:6379[2]> multi
OK
127.0.0.1:6379[2](TX)> set k5 v5
QUEUED
127.0.0.1:6379[2](TX)> discard //退出当前事务
OK
127.0.0.1:6379[2]> get k5
(nil)
127.0.0.1:6379[2]>

编译型异常(代码有问题),事务中所有的命令都不会执行

127.0.0.1:6379[2]> multi //开启事务
OK
127.0.0.1:6379[2](TX)> set k1 v1 //入队操作
QUEUED
127.0.0.1:6379[2](TX)> set k2 v2
QUEUED
127.0.0.1:6379[2](TX)> getset k2
(error) ERR wrong number of arguments for 'getset' command //出现问题
127.0.0.1:6379[2](TX)> set k3 v3
QUEUED
127.0.0.1:6379[2](TX)> exec //执行事务
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379[2]> get k3 //所有命令都不会执行
(nil)
127.0.0.1:6379[2]>

运行时异常(1/0),如果事务队列中存在不合法命令,在执行命令的时候,其他命令是可以正常执行的,错误命令抛出异常

127.0.0.1:6379[2]> set k1 v1
OK
127.0.0.1:6379[2]> multi
OK
127.0.0.1:6379[2](TX)> incr k1 //命令出错
QUEUED
127.0.0.1:6379[2](TX)> set k2 v2
QUEUED
127.0.0.1:6379[2](TX)> set k3 v3
QUEUED
127.0.0.1:6379[2](TX)> exec
1) (error) ERR value is not an integer or out of range
2) OK
3) OK
127.0.0.1:6379[2]> get k2 //其他命令正常执行
"v2"
127.0.0.1:6379[2]>

监控 watch

悲观锁:

很悲观,认为什么时候都会出问题,无论做什么都会加锁。

乐观锁:

  1. 很乐观,认为什么时候都不会出问题,所以不会上锁,在更新数据的时候会判断,在此期间是否有人修改了数据
  2. 获取version
  3. 更新的时候比较version

redis 监视测试

正常执行
127.0.0.1:6379[2]> set money 100
OK
127.0.0.1:6379[2]> set out 0
OK
127.0.0.1:6379[2]> watch money //监视money对象
ok
127.0.0.1:6379[2]> multi //开启事务
OK
127.0.0.1:6379[2](TX)> decrby money 20 //减去20
QUEUED
127.0.0.1:6379[2](TX)> incrby out 20 //增加20
QUEUED
127.0.0.1:6379[2](TX)> exec //执行事务
1) (integer) 80
2) (integer) 20
127.0.0.1:6379[2]> get money
"80"
127.0.0.1:6379[2]> get out
"20"
127.0.0.1:6379[2]>

测试多线程修改值,使用watch可以当做redis的乐观锁操作

线程一
1) (integer) 70
2) (integer) 30
127.0.0.1:6379[2]> watch money //监视money
OK
127.0.0.1:6379[2]> multi
OK
127.0.0.1:6379[2](TX)> decrby money 10
QUEUED
127.0.0.1:6379[2](TX)> incrby money 10
QUEUED
127.0.0.1:6379[2](TX)> exec //执行之前,另一个线程修改了值,就会导致事务执行失败
(nil)
127.0.0.1:6379[2]> get money //修改后的值
"200"
127.0.0.1:6379[2]> get out
"30"
127.0.0.1:6379[2]>
#############################################
另一个线程修改了值(在exec事务执行前)
127.0.0.1:6379[2]> set money 200
OK
127.0.0.1:6379[2]>

监视失败,重新监视新的值

127.0.0.1:6379[2]> UNWATCH //如果失败,先解锁
OK
127.0.0.1:6379[2]> watch money //重新监视
OK
127.0.0.1:6379[2]> multi //开启事务
OK
127.0.0.1:6379[2](TX)> decrby money 10
QUEUED
127.0.0.1:6379[2](TX)> incrby out 10
QUEUED
127.0.0.1:6379[2](TX)> exec //执行事务
1) (integer) 190
2) (integer) 40
127.0.0.1:6379[2]>

jedis

jedis 是redis官方推荐的java连接开发工具,使用java操作redis的中间件

导入依赖

<!--    jedis依赖-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>4.3.1</version>
</dependency>
<!-- fastjson 依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.21</version>
</dependency>

编码测试:

  1. 连接数据库
  2. 操作命令
  3. 断开连接
public class TestJedis {
public static void main(String[] args) {
// new jedis对象
Jedis jedis = new Jedis("127.0.0.1",6379);
System.out.println(jedis.ping());
}
}
//在此之前打开本地redis-server服务
//控制台出现PONG 连接成功

jedis事务再理解

public class TestTX {
public static void main(String[] args) {
Jedis jedis = new Jedis("127.0.0.1", 6379);

jedis.flushDB();//清空数据库
JSONObject jsonObject = new JSONObject();
jsonObject.put("hello","world");
jsonObject.put("hello1","world1");
Transaction multi = jedis.multi();
String result = jsonObject.toJSONString();
try {
multi.set("user1",result);
multi.set("user2",result);
int i=1/0;//运行时异常
multi.exec(); //执行事务
} catch (Exception e) {
multi.discard();//放弃事务
e.printStackTrace();
}finally {
System.out.println(jedis.get("user1"));
System.out.println(jedis.get("user2"));
jedis.close();//关闭连接
}
}
}

redis.conf详解

image-20230102150717489

image-20230102150845373

网络

bind 127.0.0.1 -::1 //本机ip
protected-mode yes //保护模式
port 6379 //端口

通用

daemonize yes //守护线程,默认是no,手动开启
pidfile /var/run/redis_6379.pid //后台方式运行,需要一个pid文件
####日志
# Specify the server verbosity level.
# This can be one of:
# debug (a lot of information, useful for development/testing)
# verbose (many rarely useful info, but not a mess like the debug level)
# notice (moderately verbose, what you want in production probably)
# warning (only very important / critical messages are logged)
loglevel notice
logfile "" //日志的文件夹位置
databases 16 //数据库数量,默认16
always-show-logo no //是否显示logo

快照

持久化,在规定的时间内,执行了多少次操作,则会持久化到文件.rdb .aof中

save 3600 1 //3600s内,至少一个key发生了变化,会进行持久化操作
300 100
60 10000
stop-writes-on-bgsave-error yes //持久化出错,是否继续工作
rdbcompression yes //是否压缩rdb文件
rdbchecksum yes //保存rdb文件,进行检查
dir ./ //rdb文件保存的目录

redis持久化

rdb(redis database)

rdb保存的文件是dump。rdb

image-20230102154617798

aof(append only file)

aof保存的位置appendonly.aof文件