什么是Redis
Redis是一个可基于内存亦可持久化的日志型、Key-Value的高性能nosql数据结构存储系统,可用作数据库、缓存、消息中间件。
安装Rides
1、使用宝塔安装程序
2、Linux命令行
-
下载安装包 redis -5.0.8并上传到服务器
-
解压Redis安装包
tar -zxvf redis-5.0.8.tar.gz
-
redis需要c++环境支持,安装gcc环境
yum install gcc-c++
-
安装redis环境
make
make install
-
进入redis环境的路径
cd /usr/local/bin
-
复制一份redis.conf到本目录,以后直接操作该配置文件
cp /www/server/redis/redis.conf config
-
修改配置文件redis.config: daemonize no >daemonize yes(宝塔不用改)
vim config/redis.conf
启动Redis
redis-server config/redis.conf
连接Redis
redis-cli -p 6379
-
输入ping如果输出PONG代表着测试成功
-
查看Rides启动进程
ps -ef|grep redis
-
关闭Rides服务
shutdown
exit
Redis基础知识
-
redis默认有16个数据库,默认使用的是第0个数据库,在redis.conf文件中可查看:database 16
-
redis所有命令可以去Rides中文官网 查看
-
切换数据库命令(切换到第二个数据库)
127.0.0.1:6379> select 2
OK
127.0.0.1:6379[2]>
-
查看数据库大小
127.0.0.1:6379> dbsize
(integer) 5
127.0.0.1:6379>
-
存值
127.0.0.1:6379> set name yijin
OK
127.0.0.1:6379>
-
取值
127.0.0.1:6379> get name
"yijin"
127.0.0.1:6379>
-
查看key的类型
127.0.0.1:6379[1]> type name
string
-
设置值过期时间:EXPIRE key seconds
设置key为name的值在10秒后过期
127.0.0.1:6379[1]> expire name 10
(integer) 1
-
查看值过期时间:ttl key
查看key为name的值在5秒后过期
127.0.0.1:6379[1]> ttl name
(integer) 5
-
查看该数据库中是否有key为name的值(1:存在 0:不存在)
127.0.0.1:6379[2]> EXISTS name
(integer) 0
127.0.0.1:6379[2]>
-
移除key为name的值:move key 索引(1:成功 0:失败)
127.0.0.1:6379[2]> move name 1
(integer) 1
-
查看该数据库中所有的key
127.0.0.1:6379> keys *
1) "name"
2) "mylist"
3) "key:__rand_int__"
4) "counter:__rand_int__"
5) "myset:__rand_int__"
127.0.0.1:6379>
-
清空该数据库
127.0.0.1:6379[2]> flushdb
OK
127.0.0.1:6379[2]>
-
清空所有数据库
127.0.0.1:6379[2]> FLUSHALL
OK
127.0.0.1:6379[2]>
五大数据类型
string类,可用于记录某些频繁的值
1. String
-
批量设置值:mset(如果数据库有相同的键则会设置失败)
127.0.0.1:6379[2]> mset name "yijin" age 10 sex "nv"
OK
127.0.0.1:6379[2]> keys *
1) "sex"
2) "age"
3) "name"
-
批量获取值:mget
127.0.0.1:6379[2]> mget name age sex
1) "yijin"
2) "10"
3) "nv"
-
追加字符串:append key “vaule”,如果key不存在则新建一个值
127.0.0.1:6379[1]> append name "hello"
(integer) 10
-
获取字符串长度
127.0.0.1:6379[1]> strlen name
(integer) 10
-
值+1(i++)
127.0.0.1:6379[1]> incr age
(integer) 1
-
值-1(i–)
127.0.0.1:6379[1]> decr age
(integer) 0
-
值+n: incrby age n
127.0.0.1:6379[1]> incrby age 10
(integer) 10
-
值 - n: decrby age n
127.0.0.1:6379[1]> decrby age 5
(integer) 5
-
字符串范围: getrange
127.0.0.1:6379[2]> getrange name 1 3
"iji"
-
修改字符串范围: setrange
127.0.0.1:6379[2]> setrange name 3 1
(integer) 5
-
设置一个值并设置过期时间: setex
127.0.0.1:6379[2]> setex key1 10 "hello"
OK
127.0.0.1:6379[2]> get key1
"hello"
127.0.0.1:6379[2]> ttl key1
(integer) -2
127.0.0.1:6379[2]> get key1
(nil)
-
不存在则设置值: setnx(在分布式锁经常使用)
127.0.0.1:6379[2]> keys *
1) "age"
2) "name"
127.0.0.1:6379[2]> setnx name "hekk"
(integer) 0
-
getset组合命令(如果没有值则返回nil再设置值,如果存在则获得值再赋予新的值)
127.0.0.1:6379[2]> getset name "yijing"
"yijin"
127.0.0.1:6379[2]> get name
"yijing"
2. List
基本的数据类型,list其实是一个链表结构
如果key不存在,创建一个list,存在则添加一个元素
能同时在左右两端同时操作元素,既可以作为队列,又可以作为栈
所有的list命令都为l开头
-
放入:LPUSH (将一个或多个值放入列表的左边) RPUSH(将一个或多个值放入列表右边)
127.0.0.1:6379[2]> LPUSH list1 one two three
(integer) 3
127.0.0.1:6379[2]> rpush list1 four five
(integer) 5
127.0.0.1:6379[2]> lrange list1 0 -1
1) "three"
2) "two"
3) "one"
4) "four"
5) "five"
-
移除:LPOP (移除左边的一个值) RPOP(移除右边的一个值)
127.0.0.1:6379[2]> lrange list1 0 -1
1) "three"
2) "two"
3) "one"
4) "four"
5) "five"
127.0.0.1:6379[2]> lpop list1
"three"
127.0.0.1:6379[2]> rpop list1
"five"
-
获取:LRANGE(通过具体区间获取值,如果想获取所有可用0 -1)
127.0.0.1:6379[2]> LRANGE list1 0 1
1) "three"
2) "two"
-
获取list下标的值:lindex
127.0.0.1:6379[2]> lindex list1 1
"one"
-
获取list长度:llen
127.0.0.1:6379[2]> llen list1
(integer) 3
-
移除list中的元素:lrem(指定个数)
127.0.0.1:6379[2]> lrem list1 1 one
(integer) 1
-
把一个list中最后一个元素移动到另一个list中:rpoplpush
127.0.0.1:6379[2]> lrange list1 0 -1
1) "two"
2) "four"
127.0.0.1:6379[2]> rpoplpush list1 list2
1)"four"
127.0.0.1:6379[2]> lrange list2 0 -1
1) "four"
-
修改该下标的值:lset(没有下标则报错)
127.0.0.1:6379[2]> lset list2 0 fours
OK
127.0.0.1:6379[2]> lrange list2 0 -1
1) "fours"
-
插入值:linsert(before|after)
127.0.0.1:6379[2]> lrange list2 0 -1
1) "five"
2) "fours"
127.0.0.1:6379[2]> linsert list2 after fours three
(integer) 3
127.0.0.1:6379[2]> lrange list2 0 -1
1) "five"
2) "fours"
3) "three"
3. Set
无序不重复集合,所有命令都由S开头
-
添加元素:sadd , 查看set中的元素:smembers,查看set大小:scard
127.0.0.1:6379[2]> sadd set1 hello
(integer) 1
127.0.0.1:6379[2]> smembers set1
1) "hello"
127.0.0.1:6379[2]> scard set1
(integer) 3
-
判断集合中是否存在该元素:sismember
127.0.0.1:6379[2]> sismember set1 hello
(integer) 1
-
移除集合中的元素: srem
127.0.0.1:6379[2]> srem set1 hello
(integer) 1
-
移动一个元素到另一个集合中
127.0.0.1:6379[2]> smove set1 set2 two
(integer) 1
127.0.0.1:6379[2]> smembers set2
1) "two"
-
随机获取集合中指定个数元素:srandmember
127.0.0.1:6379[2]> srandmember set1 2
1) "two"
2) "three"
-
随机删除指定个数元素:spop
127.0.0.1:6379[2]> spop set1
"one"
127.0.0.1:6379[2]> smembers set1
1) "three"
2) "two"
-
获取另一个集合中不同的元素:sdiff (差集)
127.0.0.1:6379[2]> sdiff set1 set2
1) "one"
2) "three"
-
获取两个集合中相同的元素:sinter (交集)
127.0.0.1:6379[2]> sinter set1 set2
1) "two"
-
获取两个集合中所有元素:sunion (并集)
127.0.0.1:6379[2]> sunion set1 set2
1) "one"
2) "three"
3) "two"
4. Hash
哈希,key-map,一般用于存储对象,命令以h开头
-
设置hash值:hset ,设置多个hash值:hmset
127.0.0.1:6379[2]> hset map1 name yijin
(integer) 1
127.0.0.1:6379[2]> hmset map1 age 16 sex man
OK
-
获取hash值:hget,获取多个值:hmget
127.0.0.1:6379[2]> hget map1 name
"yijin"
127.0.0.1:6379[2]> hmget map1 name age
1) "yijin"
2) "16"
-
获取hash中所有的键值:hgetall
127.0.0.1:6379[2]> hgetall map1
1) "name"
2) "yijin"
3) "age"
4) "16"
5) "sex"
6) "man"
-
获取hash中所有的键:hkeys , 获取hash中所有的值:hvals
127.0.0.1:6379[2]> hkeys map1
1) "name"
2) "age"
127.0.0.1:6379[2]> hvals map1
1) "yijin"
2) "16"
-
删除hash中的值:hdel
127.0.0.1:6379[2]> hdel map1 sex
(integer) 1
-
获取hash的大小:hlen
127.0.0.1:6379[2]> hlen map1
(integer) 2
-
判断hash中是否拥有该值
127.0.0.1:6379[2]> hexists map1 name
(integer) 1
5. Zset
有序集合,在set的基础上增加一个值
-
设置值:zadd
127.0.0.1:6379[2]> zadd zset1 1 one 2 two
(integer) 2
-
获取值:zrange,降序:zrevrange
127.0.0.1:6379[2]> zrange zset1 0 -1
1) "one"
2) "two"
127.0.0.1:6379> zrevrange zset1 0 -1 withscores
1) "two"
2) "2"
3) "one"
4) "1"
-
获取区间内的值:zrangebyscore(-inf:-无穷,+inf:+无穷)
127.0.0.1:6379> zrangebyscore zset1 -inf +inf
1) "one"
2) "two"
127.0.0.1:6379> zrangebyscore zset1 -inf +inf withscores
1) "one"
2) "1"
3) "two"
4) "2"
-
移除元素:zrem
127.0.0.1:6379> ZREM zset1 one
(integer) 1
-
获取有序集合大小:zcard
127.0.0.1:6379> ZCARD zset1
(integer) 1
-
获取指定区间的总数:zcount
127.0.0.1:6379> ZCOUNT zset1 0 2
(integer) 1
三种特殊数据类型
1、geospatial 地理位置
底层由zset实现,具体使用规则:https://www.redis.net.cn/order/3685.html
-
添加数据:geoadd,查询城市位置: http://www.jsons.cn/lngcode注:两极无法添加,一般由程序直接导入,参数:key 值(纬度,经度,地名)
127.0.0.1:6379[2]> geoadd china:city 116.40 39.90 beijin
(integer) 1
-
查找数据:geopos
127.0.0.1:6379[2]> GEOPOS china:city beijin
1) 1) "116.39999896287918091"
2) "39.90000009167092543"
-
查询两个地理位置的距离:geodist
127.0.0.1:6379[2]> geodist china:city changshaa shanghai km
"886.7051"
127.0.0.1:6379[2]> geodist china:city shanghai beijin km
"1067.3077"
-
查询该坐标半径以内中所有元素
127.0.0.1:6379[2]> georadius china:city 110 30 1000 km
1) "changshaa"
127.0.0.1:6379[2]> georadius china:city 110 30 1000 km withcoord
1) 1) "changshaa"
2) 1) "112.98227995634078979"
2) "28.19409000030395163"
127.0.0.1:6379[2]> georadius china:city 110 30 10000 km withcoord count 1
1) 1) "changshaa"
2) 1) "112.98227995634078979"
2) "28.19409000030395163"
-
查询该元素半径以内中所有元素:georadisbymember
127.0.0.1:6379[2]> GEORADIUSBYMEMBER china:city shanghai 5000 km
1) "changshaa"
2) "shanghai"
3) "beijin"
-
查询一个或多个元素的hash值:geohash
127.0.0.1:6379[2]> GEOHASH china:city shanghai changshaa beijin
1) "wtw3sjt9vg0"
2) "wt026ux4mz0"
3) "wx4fbxxfke0"
2、hyperloglog基数统计
redis2.8.9版本更新了hyperloglog数据结构,能作为网页的UV(同一账号多次访问只能算作一次访问),优点:存储2^64个不同元素只消耗12kb内存,能节省内存空间,缺点:不精确,会有一定的误差,0.81%错误率
-
添加元素:pfadd
127.0.0.1:6379[2]> pfadd demo1 a b b c c d d
(integer) 1
-
查询基数估算值:pfcount
127.0.0.1:6379[2]> PFCOUNT demo1
(integer) 4
-
将多个hyperloglog合成一个(并集+去从):pfmerge
127.0.0.1:6379[2]> PFMERGE demo3 demo1 demo2
OK
127.0.0.1:6379[2]> keys *
1) "demo3"
2) "demo2"
3) "demo1"
127.0.0.1:6379[2]> PFCOUNT demo3
(integer) 5
3、bigmaps位图运算
统计用户活跃度,签到,打卡功能,bigmaps位图是直接操作二进制
-
设置值:setbit
127.0.0.1:6379[2]> setbit bit1 1 1
(integer) 0
127.0.0.1:6379[2]> setbit bit1 2 1
(integer) 0
127.0.0.1:6379[2]> setbit bit1 3 0
(integer) 0
-
获取值:getbit
127.0.0.1:6379[2]> getbit bit1 2
(integer) 1
127.0.0.1:6379[2]> getbit bit1 3
(integer) 0
-
统计总数:bitcount
127.0.0.1:6379[2]> BITCOUNT bit1
(integer) 2
事务
Redis事务本质:一组事务的集合!一个事务中的所有命令都会被序列化,在事务的执行过程中,会按照顺序执行。会按照一次性、顺序性、排他性去执行列的命令
Redis事务没有隔离级别的概念!
所有命令在事务中,并没有直接执行,只有发起了执行命令才会执行!
Redis只有单条命令保持原子性,但事务是不保持原子性的!
Redis事务:
-
开启事务(multi)————命令入队————执行事务(exec)
127.0.0.1:6379[2]> multi
OK
127.0.0.1:6379[2]> set demo1 one
QUEUED
127.0.0.1:6379[2]> set demo2 two
QUEUED
127.0.0.1:6379[2]> get demo1
QUEUED
127.0.0.1:6379[2]> get demo2
QUEUED
127.0.0.1:6379[2]> exec
1) OK
2) OK
3) "one"
4) "two"
-
取消事务:discard
127.0.0.1:6379[2]> multi
OK
127.0.0.1:6379[2]> set demo3 three
QUEUED
127.0.0.1:6379[2]> set demo4 four
QUEUED
127.0.0.1:6379[2]> DISCARD
OK
-
事务中的异常:1、编译异常 :事务中命令有错,整个事务都会取消
127.0.0.1:6379[2]> multi
OK
127.0.0.1:6379[2]> set demo1 one
QUEUED
127.0.0.1:6379[2]> getset demo1
(error) ERR wrong number of arguments for 'getset' command
127.0.0.1:6379[2]> set demo2 two
QUEUED
127.0.0.1:6379[2]> exec
(error) EXECABORT Transaction discarded because of previous errors
-
事务中的异常:2、运行异常 :事务队列中存在语法性错误,只影响错误的那个队列,其他命令正常运行
127.0.0.1:6379[2]> lpush list one two three
(integer) 3
127.0.0.1:6379[2]> multi
OK
127.0.0.1:6379[2]> get list
QUEUED
127.0.0.1:6379[2]> set demo3 hello
QUEUED
127.0.0.1:6379[2]> get demo3
QUEUED
127.0.0.1:6379[2]> exec
1) (error) WRONGTYPE Operation against a key holding the wrong kind of value
2) OK
3) "hello"
-
监控(watch):测试多线程操作值时,watch可当做redis乐观锁操作
悲观锁:思想很悲观,不管任何操作都会上锁
乐观锁:思想很乐观,更新数据时去判断是否有人修改过这个数据,获取version,更新的时候比较version
正常情况:
127.0.0.1:6379[2]> set money 100
OK
127.0.0.1:6379[2]> set personl 0
OK
127.0.0.1:6379[2]> watch money
OK
127.0.0.1:6379[2]> multi
OK
127.0.0.1:6379[2]> DECRBY money 10
QUEUED
127.0.0.1:6379[2]> incrby personl 10
QUEUED
127.0.0.1:6379[2]> exec
1) (integer) 90
2) (integer) 10
失败情况:当线程a修改加锁的数据的时候,线程b更新了该数据,这时线程a提交事务时则会失败,事务失败则需要手动解除锁 unwatch:解除锁
127.0.0.1:6379[2]> set money 100
OK
127.0.0.1:6379[2]> watch money
OK
127.0.0.1:6379[2]> multi
OK
127.0.0.1:6379[2]> decrby money 50
QUEUED
127.0.0.1:6379[2]> exec
(nil)
该线程在线程a操作money但未提交时修改了money,导致线程a后面提交为nil
127.0.0.1:6379[2]> incrby money 1000
(integer) 1100
Jedis
官方推荐的Java连接开发工具,是一个Java操作Redis的中间件
步骤:
1、导入依赖
<dependencies>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.3.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency>
</dependencies>
2、编码测试(输出PONG代表成功),Redis数据类型操作命令都能Jedis实例中找到对应方法!
public class RedisTest {
public static void main(String[] args) {
Jedis jedis=new Jedis("云服务器IP",6379);
System.out.println(jedis.ping());
}
}
SpringBoot整合Redis
1、导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2、配置连接
spring.redis.host=127.0.0.1
spring.redis.port=6379
3、测试
@SpringBootTest
class Redis02SpringbootApplicationTests {
@Autowired
private RedisTemplate redisTemplate;
@Test
void contextLoads() {
redisTemplate.opsForValue().set("name","yijin");
System.out.println(redisTemplate.opsForValue().get("name"));
}
}