Redis学习笔记
什么是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"));
}
}