各种网站崩了的现象,由于海量用户,高并发 导致。
其中罪魁祸首就是关系型数据库:
用Redis来解决问题的思路:
Nosql(Not-Only SQL):作为关系型数据库的补充。用内存存储;不存储关系,只存数据
Nosql特征:
常见的Nosql数据库:Redis、memcache、HBase、MongoDB
数据间没有必然的关联关系,都是一个个key-value
内部采用单线程机制进行工作
redis所有的操作都是原子性的,采用单线程处理所有业务,命令是一个一个执行的,因此无需考虑并发带来的数据影响。
高性能。
多数据类型支持
持久化支持。(可以进行数据灾难恢复)
key的命名规范:(表名: 主键名: 主键值: 字段名)。eg:
set user:id:123:fansNum 200
在redis中以json格式存value值用户信息
set user:id:123 {id:123, blog:879, fans: 200}
Redis Desktop Manager
Redis几种类型的可视化工具(桌面版、web版、ide的插件版等)
windows版主要是初学者不了解linux的人用于学习redis用的,真正要用redis还是要跑在Linux上
下载地址,下载完解压即可
下载(这里下的是4.0.0的版本)
wget http://download.redis.io/releases/redis-4.0.0.tar.gz
解压
tar –xvf redis-4.0.0.tar.gz
编译 + 安装 (这一步要把目录切换到 cd redis-4.0.0/src 文件目录下)
make install
//若安装不成功,用命令(可能是用户权限不够?)
sudo apt install redis-server
到此已安装完成,可以启动server和cli测试一下
启动server和cli,然后set get测试一下
//1.在redis-4.0.0/src目录下启动服务端
redis-server
//2.启动客户端
redis-cli
指定端口启动redis服务
(当要多个redis服务时,在启服务的时候就指定端口,通过指定不同的端口指定不同的redis服务)
redis-server --port 6380
redis客户端连接时也指定端口
redis-cli -p 6380
通过配置文件启动redis服务(实现启动多台redis服务)
先在redis目录下设置配置文件(类似原有的redis-4.0.0/redis.conf),可以对应每个服务有一个配置文件
然后启动redis服务时指定配置文件
//1.切换到redis服务下
cd redis-4.0.0
//2.创建一个文件夹conf,就用来存放redis各个服务的配置文件
mkdir conf
//3.在redis-4.0.0/conf目录下,新建配置文件redis-6379.conf
vim redis-6379.conf
//打开redis-6379.conf后写一些配置
port 6379
daemonize yes
logfile "6379.log"
dir /redis-4.0.0/data
//4.以配置文件形式启动redis服务
redis-server conf/redis-6379.conf
//启动几个redis服务设置几个配置文件,然后几个redis服务就可以在同一个机器上跑
redis配置文件中的一些配置内容
string类型,一个存储空间保存一个数据,最简单的数据类型,通常使用字符串,若字符串以整数的形式展示,可以作为数字操作使用,但他还是字符串
string的基本操作
Redis客户端cmd操作:
set key value //设置(原有的覆盖,没有的追加)
get key //获取
del key //删除
mset key1 value1 key2 value2… //添加修改多个数据 multiple
mget key1 key2… //获取多个数据
strlen key //获取数据字符个数(字符串长度)
append key value //追加到原始信息后部(若原始信息存在就追加,否则新建)
string的单数据操作和多数据操作怎么选择(set 和 mset)
一条指令的执行时间 = 指令发送给redis的时间 + redis执行指令的时间 + redis返回执行结果的时间
根据消耗的时长,选择set还是mset
string的扩展操作
设置数值增加或减少指定范围的值
//设置数值增加或减少指定范围的值
incr key //+1
decr key //-1
incrby key increment //+increment
decrby key increment //-increment
incrbyfloat key increment
//注意,若原始数据不能转为数值或超越了redis数值上线范围,将报错
//这个方案:可以用redis来控制数据库表主键id,为数据库表主键提供生成策略,保证主键唯一性
设置数据具有指定的生命周期
setex key seconds value
psetex key millisecends value
hash类型,一个存储空间保存多个键值对数据,底层使用哈希表结构实现数据存储。适用场景:适用存一个表中的多个字段的值,把各个字段搞在一个里面,又要方便获取修改
hash类型,指key-value中的value是hash类型的,即value又是一个key-value类型。
即结构是key-{fields-values}。 key-hash{field1-value1; field2-value2;field3-value3…}
注意:hash类型下的value只能存字符串,不允许存其它类型的数据
应用场景eg:电商网站购物车:用户id做key,商品id做field,数量做value
hash的基本操作 (在redis客户端的cmd指令)
//1.添加修改数据
hset key field value
//2.获取数据
hget key field
hgetall key
//3.删除数据
hdel key field1 [field2]
//4.添加修改多个数据
hmset key field1 value1 field2 value2 …
//5.获取多个数据
hmset key field1 field2 …
//6.获取哈希表中字段的数量
hlen key
//7.获取哈希表中是否存在指定的字段
hexists key field
hash数据的扩展操作
//8.获取哈希表中所有的字段名(field)或字段值(value)
hkeys key
hvals key
//9.设置指定字段的数值数据 增加指定范围的值
hincrby key field increment
hincrbyfloat key field increment
list类型 [ key - list ]
数据存储需求:存储多个数据,并对数据进入存储空间的顺序进行区分
需要的存储结构:一个存储空间保存多个数据,且通过数据可以体现进入顺序
list类型:保存多个数据,底层使用双向链表存储结构实现
应用场景举例:(朋友圈的点赞列表存储)
朋友圈是key,点赞列表是:list类型的value,点赞操作是:rpush,取消点赞lrem
redis 应用于具有操作先后顺序的数据控制
list类型的操作
//1.添加/修改数据
lpush key value1 [value2] …… //从左边进数据
rpush key value1 [value2] …… //右边进
//2.获取数据
lrange key start stop //取一个范围内的多个数据 (注意有顺序的概念)
//当不知道list总共有多少个数据时,可以用 lrange list1 0 -1 (-1表示倒数第一个)
lindex key index
llen key //list长度
//3.获取并移除元素
lpop key
rpop key
//4.规定时间内获取并移除数据 (b代表block阻塞)
blpop key1 [key2] timeout
brpop key1 [key2] timeout
brpoplpush source destination timeout
//阻塞的获取数据理解:现在的list中没有这个数据,不代表以后这个list中没有这个数据。
//blpop表示可以等timeout时间范围内只要有这个数据就获取并移除。
//key1 [key2] 表示可以从多个list中等待找这个数据 (理解像多个任务队列中取任务一样)
//5.移除指定数据
lrem key count value //count表示移除几个相同的元素
list的注意事项
set类型
存储大量的数据,在查询方面提供更高的效率(比起list存储效率高,因为list底层是链表,不利于查询)
set类型底层结构:与hash存储结构完全相同,仅存储键,不存储值(nil),并且值是不允许重复的
理解底层结构,set整个是key - hash,其中hash的key-value中的key存真正的数据,value不存东西
Java中的HashSet 的内部实现使用的是HashMap,只不过所有的value都指向同一个对象
Redis的Set结构也是一样,它的内部也是用hash结构,所有的value都指向同一个内部值
其实就是不允许重复的集合
set类型的基本操作
//1.添加数据
sadd key member1 [member2]
//2.获取全部数据
smembers key
//3.删除数据
srem key member1 [member2]
//4.获取集合数据总量 (该set集合中有几个数据)
scard key
//5.判断集合中是否包含指定数据
sismember key member
set类型的扩展操作
//6.随机获取集合中指定数量的数据 (原集合数据不变)
srandmember key [count]
//7.随机获取集合中的某个数据并将该数据移出集合 (原集合数据改变)
spop key [count]
//8.求两个集合的交、并、差集
sinter key1 [key2]
sunion key1 [key2]
sdiff key1 [key2]
//9.求两个集合的交、并、差集并存储到指定集合中
sinterstore destination key1 [key2]
sunionstore destination key1 [key2]
sdiffstore destination key1 [key2]
//10.将指定数据从原始集合中移动到目标集合中
smove source destination member
set的注意事项
sorted_set类型
数据排序有利于数据的有效展示,需要提供一种可以根据自身特征进行排序的方式
sorted_set类型:在set的存储结构基础上添加可排序字段
底层数据类型:key - hash( value - nil - score),会根据score大小进行排序
sorted_set 底层存储还是基于set结构的,因此数据不能重复,如果重复添加相同的数据,score值将被反复覆盖,保留最后一次修改的结果
sorted_set基本操作
//1.添加数据
zadd key score1 member1 [score2 member2]
//2.获取全部数据
zrange key start stop [WITHSCORES]
zrevrange key start stop [WITHSCORES]
//不加withscores会展示排好序的数据,加withscores展示排好序的数据和分数
//zrange是正向从小到大的顺序,zrevrange是从大到小
//3.删除数据
zrem key member [member ...]
//4.按条件获取数据
zrangebyscore key min max [WITHSCORES] [LIMIT]
zrevrangebyscore key max min [WITHSCORES]
//5.条件删除数据
zremrangebyrank key start stop
zremrangebyscore key min max
//min与max用于限定搜索查询的条件
//start与stop用于限定查询范围,作用于索引,表示开始和结束索引
//6.获取集合数据总量
zcard key
zcount key min max
//7.集合交、并操作
zinterstore destination numkeys key [key ...]
zunionstore destination numkeys key [key ...]
//8.获取数据对应的索引(排名)
zrank key member
zrevrank key member
//9.score值获取与修改
zscore key member
zincrby key increment member
//1.删除指定key
del key
//2.获取key是否存在
exists key
//3.获取key的类型
type key
//4.为指定key设置有效期
expire key seconds
pexpire key milliseconds //pexpire和expire区别是时间单位不同
expireat key timestamp //3和4指令使用时间戳,linux系统中用的
pexpireat key milliseconds-timestamp
//5.获取key的有效时间
ttl key
pttl key
//6.切换key从时效性转换为永久性
persist key
//7.查询key
keys pattern
/**
pattern 写查询模式规则
* 匹配任意数量的任意符号
?配合一个任意符号
[] 匹配一个指定符号
举例:
keys * 查询所有
keys it* 查询所有以it开头
keys *heima 查询所有以heima结尾
keys ??heima 查询所有前面两个字符任意,后面以heima结尾
keys user:? 查询所有以user:开头,最后一个字符任意
keys u[st]er:1 查询所有以u开头,以er:1结尾,中间包含一个字母,s或t
*/
//8.为key改名
rename key newkey //redis中若已有新名字则会覆盖
renamenx key newkey //若新名字不存在的时候改成功,若redis里已有新名字则不成功
//9.对所有key排序
sort
//10.其他key的通用操作
help @generic
Redis数据库
Redis为每个服务提供有16个数据库,编号index从0到15,每个数据库之间的数据相互独立
(感觉类似mysql中的schema的概念)
Redis数据库的操作
//1.切换数据库 (index是数据库编号,从0开始)
select index
//2.其他操作
quit
ping //测试redis服务器是否连通
echo message //输入什么message,控制台就会输出什么message
//3.数据移动
move key db //把key移动到db数据库中,原数据库不存在了(剪切操作)
//4.数据清除
dbsize //看当前的数据库下有多少个数据
flushdb //删掉当前数据库下的所有数据
flushall //删掉所有数据库中的所有数据
Java语言连接redis服务的工具有:Jedis、SpringData Redis、Lettuce等。其他语言也有自己连redis的工具
Jedis:使用java语言连接redis服务
Jedis官方API
Jedis使用步骤
引入jedis的jar包—— redis.clients.jedis
写代码
<dependency>
<groupId>redis.clientsgroupId>
<artifactId>jedisartifactId>
<version>2.9.0version>
dependency>
@Test
public void testJedis(){
//1.连接redis
Jedis jedis = new Jedis("127.0.0.1", 6379);
//2.操作redis
jedis.set("name","itheima");
String name = jedis.get("name");
System.out.println(name);
//3.关闭连接
jedis.close();
}
Jedis工具类开发
上述代码中,Jedis对象是手工管理的,不应该。
应该从Jedis连接池获取Jedis对象,而Jedis已经为我们提供好了连接池技术JedisPool
public class JedisUtils {
private static JedisPool jedisPool = null;
//把连接池的创建过程放在静态代码块中,就加载一次,也就创建一次
static {
/**
* JedisPool的三个参数
* 1.poolConfig:连接池配置对象
* 2.host:redis服务地址
* 3.port:redis服务端口号
*/
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
//设置最大连接数
jedisPoolConfig.setMaxTotal(30);
//设置活动连接数
jedisPoolConfig.setMaxIdle(10);
//设置redis服务地址
String host = "127.0.0.1";
//设置redis服务的端口号
int port = 6379;
//定义一个Jedis连接池
jedisPool = new JedisPool(jedisPoolConfig, host, port);
}
public static Jedis getJedis() {
//getResource()即获取Jedis对象
return jedisPool.getResource();
}
//测试一下
public static void main(String[] args) {
Jedis jedis = JedisUtils.getJedis();
}
}
位于spring-data-redis-x.x.x.jar包中,[Spring Data Redis官网](https://docs.spring.io/spring-data/redis/docs/current/reference/html/)。使用方法如下
引入jar包
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
代码中
@Service
public class LoginServiceImpl implements LoginService {
@Autowired
private RedisTemplate redisTemplate;
……
//把token放在redis里,过期时间1天
ValueOperations valueOperations = redisTemplate.opsForValue();
valueOperations.set(key, value, 1, TimeUnit.DAYS);
……
}