1、概述
redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。Redis 是一个高性能的key-value数据库。 redis的出现,很大程度补偿了memcached这类key/value存储的不足,在部 分场合可以对关系数据库起到很好的补充作用。它提供了Java,C/C++,C#,PHP,JavaScript,Perl,Object-C,Python,Ruby,Erlang等客户端,使用很方便。 [1] Redis支持主从同步。数据可以从主服务器向任意数量的从服务器上同步,从服务器可以是关联其他从服务器的主服务器。这使得Redis可执行单层树复制。存盘可以有意无意的对数据进行写操作。由于完全实现了发布/订阅机制,使得从数据库在任何地方同步树时,可订阅一个频道并接收主服务器完整的消息发布记录。同步对读取操作的可扩展性和数据冗余很有帮助。redis的官网地址,非常好记,是redis.io。(域名后缀io属于国家域名,是british Indian Ocean territory,即英属印度洋领地),Vmware在资助着redis项目的开发和维护。
2、特性
Redis 与其他 key - value 缓存产品有以下三个特点:
Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储。
Redis支持数据的备份,即master-slave模式的数据备份。
Redis官网
Redis中文官网
3、Linux下安装Redis
下载最新Redis压缩包
1、
2、基本环境
yml install gcc-c++
3、查看gcc版本
gcc -v
4、make命令配置基本环境
make
make install
5、Redis默认安装路径
usr /local/bin
6、Redis默认不是后台启动的,修要修改配置文件
进入配置文件
cd /myconf
7、启动Redis服务
redis-server myconfig/redis.conf
redis-cli -p 6379
8、测试Redis进程
ps -ef|grep redis
9、关闭Redis
#关闭Redis
shutdown
#退出
exit
4、Redis基础知识
基本指令
dbsize #查看库大小
keys * #查找所有的key
flushdb# 清空当前数据库
flushall#清除所有的数据库
Redis是单线程的
Redis是基于内存操作的,Redis的瓶颈是根据机器的内存和网络带宽,既然可以使用单线程来实现,所有就使用单线程了
Redis是将所有的数据全部放在内存中的,所有说使用单线程去操作效率就是最高的,多线程(CPU上下文会切换)
5、Redis五种基本数据类型
5.0Redis-key
5.1String(字符串)
127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3 # 同时设置多个值
OK
127.0.0.1:6379> keys *
1) "key1"
2) "mykey"
3) "k3"
4) "k1"
5) "key2"
6) "k2"
127.0.0.1:6379> flushall #1清空数据库
OK
127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3 #同时取出多个值
OK
127.0.0.1:6379> keys *
1) "k2"
2) "k3"
3) "k1"
127.0.0.1:6379> mget k1 k2 k3
1) "v1"
2) "v2"
3) "v3"
127.0.0.1:6379> msetnx k1 v1 k4 v4 #msetnx 是一个原子性的操作,要么都成功,要么都失败
(integer) 0
127.0.0.1:6379> keys *
1) "k2"
2) "k3"
3) "k1"
set user:1{name:zhangsan,age 3} #设置一个user:1对象,值为JSON字符串来保存一个对象
#可以这样写
#这里的key是一个巧妙的设计user:{id}:{filed},如此设计是完全可以的
127.0.0.1:6379> mset user:1:name zhangsan user:1:age 2
OK
127.0.0.1:6379> mget user:1:name user:1:age
1) "zhangsan"
2) "2"
###########
getset#先get在set
127.0.0.1:6379> getset db redis #如果不存在,返回nil
(nil)
127.0.0.1:6379> get db#如果存在值,获取原来的值,并设置新的值
"redis"
127.0.0.1:6379> getset db hzb
"redis"
127.0.0.1:6379> get db
"hzb"
5.2List
127.0.0.1:6379> lpush list one #向一个值或者多个值,插入到列表的头部(左边)
(integer) 1
127.0.0.1:6379> lpush list two
(integer) 2
127.0.0.1:6379> lpush list three
(integer) 3
127.0.0.1:6379> lrange list 0 -1 #取出list中的全部数据
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> lrange list 0 2 # 取出0-2范围内的数据
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> lrange list 0 1 #取出0-1范围内的数据
1) "three"
2) "two"
127.0.0.1:6379> rpush list four #放入到右边
(integer) 4
127.0.0.1:6379> lrange list 0 -1 #获取所有的数据,four在one的前面
1) "three"
2) "two"
3) "one"
4) "four"
127.0.0.1:6379> lrange list 0 -1 #显示list中的所有元素
1) "three"
2) "two"
3) "one"
4) "four"
127.0.0.1:6379> lpop list #移除左边第一个元素
"three"
127.0.0.1:6379> rpop list #移除右边第一个元素
"four"
127.0.0.1:6379> lrange list 0 -1 #显示所有元素
1) "two"
2) "one"
127.0.0.1:6379> lindex list 0 #通过下标获取某个值
"two"
127.0.0.1:6379> llen list #list的返回长度
(integer) 2
127.0.0.1:6379> ltrim list 1 2 # 通过下班班截取指定的长度,截断了只剩下截取的元素
127.0.0.1:6379> rpoplpush list mylist #移除列表中的最后一个元素,将他移动到新的列表中
127.0.0.1:6379> lset list 0 hyy #将列表中的指定下标的值替换为另一个值,更新操作
Set(里面的值不能重复)
127.0.0.1:6379> flushdb #清空数据库
OK
127.0.0.1:6379> sadd myset hello #向Set集合里面添加元素
(integer) 1
127.0.0.1:6379> sadd myset hello1 #向Set集合里面添加元素
(integer) 1
127.0.0.1:6379> sadd myset hello2 #向Set集合里面添加元素
(integer) 1
127.0.0.1:6379> smembers myset #查询集合中的元素
1) "hello1"
2) "hello"
3) "hello2"
127.0.0.1:6379> sismember myset hello #判断集合中的元素是否存在
(integer) 1
127.0.0.1:6379> sismember myset hello5
(integer) 0
127.0.0.1:6379> scard myset #获取set集合中的个数
127.0.0.1:6379> srem myset hello1 #移除set集合中的指定元素
127.0.0.1:6379> srandmember myset #随机抽选出一个元素
127.0.0.1:6379> spop myset #随机删除一些set集合中的元素
127.0.0.1:6379> clear
127.0.0.1:6379> sadd myset hello #向set集合中添加元素
(integer) 1
127.0.0.1:6379> sadd myset world #向set集合中添加元素
(integer) 1
127.0.0.1:6379> sadd myset hzb #向set集合中添加元素
(integer) 1
127.0.0.1:6379> sadd myset2 set2 #向set集合中添加元素
(integer) 1
127.0.0.1:6379> smove myset myset2 hello #将myset集合中的hello元素移到myset2中
(integer) 1
127.0.0.1:6379> smembers myset #查询myset集合中的元素
1) "hzb"
2) "world"
127.0.0.1:6379> smembers myset2 #查询myset2集合中的元素
1) "hello"
2) "set2"
127.0.0.1:6379> sdiff key1 key2 #差集
1) "a"
2) "b"
127.0.0.1:6379> sinter key1 key2 #交集
1) "c"
127.0.0.1:6379> sunion key1 key2 #并集
1) "d"
2) "b"
3) "c"
4) "a"
5) "e"
Hash
看做Map集合
127.0.0.1:6379> hset myHash field1 hzbb #向Hash集合添加
(integer) 1
127.0.0.1:6379> hget myHash field1 #取出
"hzbb"
127.0.0.1:6379> hmset myHash field1 hzb field2 hzb1 #批量添加
OK
127.0.0.1:6379> hmget myHash field1 field2 #批量取出
1) "hzb"
2) "hzb1"
127.0.0.1:6379> hgetall myHash #获取全部的数据
1) "field1"
2) "hzb"
3) "field2"
4) "hzb1"
127.0.0.1:6379> hdel myHash field1 #删除Hash指定的key,对应的value也没有了
(integer) 1
127.0.0.1:6379> hgetall myHash #获取所有的
1) "field2"
2) "hzb1"
127.0.0.1:6379> hlen myHash #判断hash中的长度
(integer) 1
127.0.0.1:6379> hexists myHash field1 #判断Hash中的指定的值是否存在
(integer) 0
127.0.0.1:6379> hexists myHash field2 #判断Hash中的指定的值是否存在
(integer) 1
127.0.0.1:6379> hvals myHash #获取Hash中的值
1) "hzb1"
Zset(有序集合)
127.0.0.1:6379> zadd myset 1 one #向Zset集合添加一个元素
(integer) 1
127.0.0.1:6379> zadd myset 2 two
(integer) 1
127.0.0.1:6379> zrange myset 0 -1 #查询集合中的所有元素
1) "one"
2) "two"
127.0.0.1:6379> zadd salary 2500 xiaoming #添加用户1
(integer) 1
127.0.0.1:6379> zadd salary 5000 xiaohuang #添加用户2
(integer) 1
127.0.0.1:6379> zadd salary 50000 hzb #添加用户3
(integer) 1
127.0.0.1:6379> zrangebyscore salary 0 5000 #排序
1) "xiaoming"
2) "xiaohuang"
127.0.0.1:6379> zrangebyscore salary 0 -1
(empty list or set)
127.0.0.1:6379> zrangebyscore salary 0 withscores
(error) ERR min or max is not a float
127.0.0.1:6379> zrangebyscore salary -inf +inf withscores #排序带数
1) "xiaoming"
2) "2500"
3) "xiaohuang"
4) "5000"
5) "hzb"
6) "50000"
127.0.0.1:6379> zcard salary #获取有序集合的个数
(integer) 3
三种特殊数据类型
geospatial地理位置
可以实现朋友定位,附近的人,打车距离计算
相关命令
GEOADD
GEODIST
GEOHASH
GEOPOS
GEORADIUS
GEORADIUSBYMEMBER
127.0.0.1:6379> GEOADD china:city 116.397128 39.916527 beijing #添加城市经纬度
(integer) 1
127.0.0.1:6379> GEOADD china:city 113.88308 22.55329 shenzhen #添加城市经纬度
(integer) 1
127.0.0.1:6379> GEOADD china:city 121.48941 31.40527 shanghai #添加城市经纬度
(integer) 1
127.0.0.1:6379> GEOADD china:city 120.21201 30.2084 hangzhou #添加城市经纬度
(integer) 1
127.0.0.1:6379> GEOADD china:city 113.27324 23.15792 guangzhou #添加城市经纬度
(integer) 1
127.0.0.1:6379> GEOADD china:city 113.6401 34.72468 zhengzhou #添加城市经纬度
(integer) 1
127.0.0.1:6379> geopos china:city beijing #获取某个城市的经纬度
1) 1) "116.39712899923324585"
2) "39.91652647362980844"
127.0.0.1:6379> geodist china:city beijing shenzhen #两个城市之间的距离
"1945754.4147"
127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km #查询以该经纬度为中心,周围1000km的城市
1) "shenzhen"
2) "guangzhou"
3) "hangzhou"
4) "zhengzhou"
Hyperloglog
统计一周的打卡
127.0.0.1:6379> setbit sign 0 1
(integer) 0
127.0.0.1:6379> setbit sign 1 0
(integer) 0
127.0.0.1:6379> setbit sign 3 1
(integer) 0
127.0.0.1:6379> setbit sign 4 0
(integer) 0
127.0.0.1:6379> setbit sign 5 1
(integer) 0
127.0.0.1:6379> setbit sign 6 1
(integer) 0
127.0.0.1:6379> getbit sign 3
(integer) 1
127.0.0.1:6379> getbit sign 4
(integer) 0
127.0.0.1:6379> bitcount sign #统计未迟到的数量
(integer) 4
事务
Redis单条命令式不保存原子性,但是事务不不保证原子性
Redis的本质:一组命令的集合,一个事务的所有命令都会被序列化,在事务执行过程中,会按照顺序执行
开启事务(multi )
命令入队
执行事务(exec)
127.0.0.1:6379> multi #开启事务
OK
127.0.0.1:6379> set k1 v1 #事务入队
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> get k3
QUEUED
127.0.0.1:6379> exec #执行事务
1) OK
2) OK
3) OK
4) "v3"
127.0.0.1:6379> discard #取消事务
监控(Watch)
乐观锁:什么时候都会出问题,无论做什么都加锁
Redis测试监控
127.0.0.1:6379> set money 100
OK
127.0.0.1:6379> set out 0
OK
127.0.0.1:6379> watch money#监控money对象
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby money 20
QUEUED
127.0.0.1:6379> incrby out 20
QUEUED
127.0.0.1:6379> exec
1) (integer) 80
2) (integer) 20
悲观锁:认为什么时候都不会出问题,所有不上锁,更新数据的时候去判断一下
Jedis
1、导入依赖
<!--导入jedis包-->
<dependencies>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency>
</dependencies>
2、编码测试
·连接数据库
常用Api
public static void main(String[] args) {
Jedis jedis = new Jedis("120.79.209.239",6379);
JSONObject jsonObject=new JSONObject();
jsonObject.put("hello","world");
jsonObject.put("name","hzb");
Transaction multi = jedis.multi();
String result = jsonObject.toJSONString();
// jedis.watch("result");//监控事务
//开启事务
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();//关闭事务
}
}
SpringBoot整合
源码分析
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")//我们可以自定义一个替换
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
//默认的RedisTemplete没有过多的设置,redis对象都需要序列化
//两个泛型都是object,object的类型,后面我们使用需要强制转换<String,String>
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
//string是最常用的,所以提供了方法
@Bean
@ConditionalOnMissingBean
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
#redis配置
spring.redis.host=localhost
spring.redis.port=6379
redisTempletepeiz
@Bean
@SuppressWarnings("all")
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory)
throws UnknownHostException {
RedisTemplate<String, Object> template = new RedisTemplate<String,Object>();
template.setConnectionFactory(factory);
//Json序列化配置
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
ObjectMapper om=new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
//String的序列化
StringRedisSerializer stringRedisSerializer=new StringRedisSerializer();
//key采用String的序列化方式
template.setKeySerializer(stringRedisSerializer);
//hash的key也采用String的序列方式
template.setHashKeySerializer(stringRedisSerializer);
//value序列化方式采用jackson
template.setValueSerializer(jackson2JsonRedisSerializer);
//hash的value序列方式采用jackson方式
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
Redis.conf详解
1、配置文件对大小写是不敏感的
可以包含多个配置文件
网络配置
端口
Redis持久化(重点)
RDB(Redis DataBase)和AOF(Append Only File)
[RDB和AOF详解](RDB(Redis DataBase)和AOF(Append Only File)。)
Redis的发布订阅
订阅:
127.0.0.1:6379> PSUBSCRIBE hzb #订阅一个频道
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "hzb"
3) (integer) 1
1) "pmessage"
2) "hzb"
3) "hzb"
4) "hzbiscool"
1) "pmessage"
2) "hzb"
3) "hzb"
4) "hellohzb"
发布端
127.0.0.1:6379> publish hzb hzbiscool #发布者发送信息到指定的频道
(integer) 1
127.0.0.1:6379> hello redis
(error) ERR unknown command `hello`, with args beginning with: `redis`,
127.0.0.1:6379> publish hzb hellohzb
(integer) 1
127.0.0.1:6379>
redis主从复制
和Mysql主从复制的原因一样,Redis虽然读取写入的速度都特别快,但是也会产生读压力特别大的情况。为了分担读压力,Redis支持主从复制,Redis的主从结构可以采用一主多从或者级联结构,Redis主从复制可以根据是否是全量分为全量同步和增量同步。下图为级联结构。
环境配置
127.0.0.1:6379> info replication #查看当前库的信息
# Replication
role:master #角色
connected_slaves:0 #从机个数
master_replid:04b2e43a66c6ad66c1231ccfdd76633c3bbf234f
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0