Redis学习笔记

Redis

NoSql概述

现在是属于大数据时代,大数据一般的数据库无法进行分析处理

  1. 单机MySQL,问题:
  • 数据量如果太大,一个机器放不下
  • 数据的索引(B+Tree),一个机器内存也放不下
  • 访问量太大(读写混合),一个服务器承受不了
  1. Memcached(缓存) + MySQL+ 垂直拆分(读写分离)

网站80%的情况都是在读,每次都要去查询数据库,十分麻烦。因此使用缓存可以减轻压力(读压力)

发展过程:优化数据结构和索引–>文件缓存(IO)–>Memcached(当时最热门的技术!)

  1. 分库分表 + 水平拆分

早些年MyISAM:表锁,十分影响效率

Innodb:行锁

后面使用分库分表解决写的压力

mysql集群

  1. 如今时代

MySQL等关系型数据库不够用了,数据量很多,变化很快

如果使用MySQL存储一些比较大的文件,博客,图片等!数据库表很大,效率就低了!

为什么要用NoSQL

用户个人信息,社交网络,地理位置,用户自己产生的数据,用户日志等爆发

这时候就要使用NoSQL

什么是NoSQL

Not Only SQL

泛指非关系型数据库

NoSQL特点

  1. 方便扩展(数据之间没有关系,很好扩展)
  2. 大数据量高性能(redis一秒写8万次,读11万次,细粒度的缓存,性能会比较高!)
  3. 数据类型多样(不需要实现设计数据库,随取随用)
  4. NoSQL:
    1. NoSQL没有固定的查询语言
    2. 键值对存储,列存储,文档存储,图形数据库
    3. CAP定理和BASE
    4. 高性能,高可用,高可扩

3V和3高

大数据时代的3V:

  • 海量Volume
  • 多样Variety
  • 实时Velocity

大数据时代的3高:

  • 高并发
  • 高可拓(随时水平拆分)
  • 高性能
  1. 商品的基本信息:
    • 名称、价格、商品信息
    • 关系型数据库解决
  2. 商品的描述、评论(文字比较多)
    • 文档型数据库,MongoDB
  3. 图片
    • 分布式文件系统 FastDFS
    • 淘宝自己的 TFS
    • Gooale的 GFS
    • Hadoop HDFS
    • 阿里云的 oss
  4. 商品的关键字(搜索)
    • 搜索引擎 solr elasticsearch
    • ISearch:多隆
  5. 商品热门的波段信息
    • 内存数据库
    • Redis ,Tair, Memache
  6. 商品的交易,外部的支付接口
    • 第三方应用

NoSQL的四大分类

KV键值对:

  • 新浪:Redis
  • 美团:Redis+Tair
  • 阿里,百度:Redis+Memecache

文档型数据库(bson格式和json一样):

  • MongoDB(一般必须要掌握)
    • Mongodb是一个基于分布式文件存储的数据库,主要用来处理大量的文档
    • MongoDB一个介于关系型数据库和非关系型数据库中中间的产品!MongoDB是非关系型数据库中最像关系型数据库的!
  • ConthDB

列存储数据库

  • HBase
  • 分布式文件系统

图关系数据库

  • 不是存图形,而是存关系,比如:朋友圈,广告推荐

Redis入门

是什么?

Remote Dictionary Server,远程字典服务,key-value数据库,免费开源,C语言编写

能干吗

  • 内存存储、持久化、内存中是断电即失(rdb、aof)
  • 效率高,可用于高速缓存
  • 发布订阅系统
  • 地图信息分析
  • 计时器,计数器

特性

  • 多样的数据类型
  • 持久化
  • 集群
  • 事务

Linux安装:

宝塔面板无脑安装,然后进入/home/www/server/redis目录下,

执行redis/src/redis-server redis.conf开启服务(如果被关闭了就要开启)

执行redis-cli命令进入redis命令行

**退出Redis:**exit

也可以使用shutdown 关闭服务器

Redis基础知识

127.0.0.1:6379> SELECT 3  # 选择数据库(默认有16个)
OK
127.0.0.1:6379[3]> SELECT 0
OK
127.0.0.1:6379> DBSIZE # 查看当前数据库有多少条数据
(integer) 1
127.0.0.1:6379> 

 
127.0.0.1:6379> FLUSHDB # 清空当前数据库
OK
127.0.0.1:6379> KEYS * # 查看当前数据库所有的key
(empty array)
127.0.0.1:6379> SET name sen # 添加键值对
OK
127.0.0.1:6379> set age 18
OK
127.0.0.1:6379> GET name # 根据key获取value
"sen"
127.0.0.1:6379> KEYS *
1) "name"
2) "age"
127.0.0.1:6379> FLUSHALL # 清空所有数据库的数据
OK

Redis是单线程的

Redis是基于内存操作,cpu不是Redis的性能瓶颈,瓶颈是根据机器的内存和网络带宽

Redis为什么单线程还这么快?

Redis是将所有的数据全部放在内存中

Redis五大数据类型

Redis-Key
127.0.0.1:6379> SET name sen
OK
127.0.0.1:6379> EXISTS name
(integer) 1 # 存在返回1,否则返回0
127.0.0.1:6379> MOVE name 1 # 移动这个key,后面跟着的1代表当前数据库
(integer) 1

127.0.0.1:6379> SET name sen
OK
127.0.0.1:6379> EXPIRE name 10 # 设置过期时间,10s
(integer) 1
127.0.0.1:6379> ttl name # 查看还剩多久过期
(integer) 5
127.0.0.1:6379> ttl name
(integer) 3
127.0.0.1:6379> ttl name
(integer) 2
127.0.0.1:6379> ttl name
(integer) 0
127.0.0.1:6379> GET name
(nil)

127.0.0.1:6379> SET age 18
OK
127.0.0.1:6379> TYPE age  # 查看key类型
string

Strings
127.0.0.1:6379> SET name hello
OK
127.0.0.1:6379> APPEND name ,world  # 追加值
(integer) 11
127.0.0.1:6379> get name
"hello,world"

127.0.0.1:6379> APPEND age 18 # 如果key不存在,就相当于SET
(integer) 2
127.0.0.1:6379> GET age
"18"


127.0.0.1:6379> STRLEN name # 获取长度
(integer) 11

增加减少:

127.0.0.1:6379> set views 0
OK
127.0.0.1:6379> INCR views # 增1
(integer) 1
127.0.0.1:6379> INCR views
(integer) 2
127.0.0.1:6379> GET views
"2"
127.0.0.1:6379> DECR views # 减1
(integer) 1
127.0.0.1:6379> DECR views
(integer) 0
127.0.0.1:6379> DECR views
(integer) -1
127.0.0.1:6379> INCRBY views 10 # 设置步长
(integer) 9
127.0.0.1:6379> INCRBY views 10
(integer) 19
127.0.0.1:6379> DECRBY views 5
(integer) 14

获取部分字符串

127.0.0.1:6379> set key1 hello,world
OK
127.0.0.1:6379> GETRANGE key1 0 5 # 闭区间[0,5]
"hello,"
127.0.0.1:6379> GETRANGE key1 0 -1 # 全部
"hello,world"

127.0.0.1:6379> SETRANGE key1 1 xxxx # 替换指定开始的字符串
(integer) 11
127.0.0.1:6379> GET key1
"hxxxx,world"

条件设置

127.0.0.1:6379> SETEX key2 30 hello # 设置key2的值30s过期
OK

127.0.0.1:6379> SETNX key redis # key不存在才设置,返回1表示设置成功
(integer) 1
127.0.0.1:6379> SETNX key mongoGB # 返回0表示设置失败
(integer) 0
127.0.0.1:6379> GET key
"redis"

批量设置和获取, mset, mget

127.0.0.1:6379> MSET k1 v1 k2 v2 k3 v3
OK
127.0.0.1:6379> MGET k1 k2 k3
1) "v1"
2) "v2"
3) "v3"
127.0.0.1:6379> MSETNX k1 hello k4 v4 # 原子性操作,要不全部成功,要么全都不成功
(integer) 0

key还可以用:分割,如(user: id: ​field)

127.0.0.1:6379> set user:1:name sen
OK
127.0.0.1:6379> set user:1:age 18
OK
127.0.0.1:6379> get user:1:age
"18"
127.0.0.1:6379> get user:1:name
"sen"

getset

127.0.0.1:6379> getset db redis # 不存在返回空,再设置
(nil)
127.0.0.1:6379> get db
"redis"
127.0.0.1:6379> getset db mongoDB # 存在返回先前的值再设置
"redis"
127.0.0.1:6379> get db
"mongoDB"
Lists

Redis的list可作为栈、队列、阻塞队列

所有命令大多都是以 L 开头的

左插入LPUSH 和 右插入RPUSH ,左弹出LPOP和右弹出RPOP,查看指定索引的值LINDEX, 查看列表长度LLEN

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 # 查看列表所有的的元素
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> LRANGE list 0 1
1) "three"
2) "two"
127.0.0.1:6379> RPUSH list zero # 从右边插入
(integer) 4
127.0.0.1:6379> LRANGE list 0 -1
1) "three"
2) "two"
3) "one"
4) "zero"

127.0.0.1:6379> LPOP list # 弹出左边第一个值
"three"
127.0.0.1:6379> RPOP list # 弹出右边第一个值
"zero"
127.0.0.1:6379> LRANGE list 0 -1
1) "two"
2) "one"
127.0.0.1:6379> LINDEX 0
(error) ERR wrong number of arguments for 'lindex' command
127.0.0.1:6379> LINDEX list 0 # 查看指定索引的值
"two"
127.0.0.1:6379> LINDEX list 1
"one"

127.0.0.1:6379> LLEN list 
(integer) 2

移动指定的值LREM

127.0.0.1:6379> lpush list three
(integer) 3
127.0.0.1:6379> lpush list three two
(integer) 5
127.0.0.1:6379> lrange list 0 -1
1) "two"
2) "three"
3) "three"
4) "two"
5) "one"

127.0.0.1:6379> LREM list 2 two  # 移出指定的值,中间的count为数量
(integer) 2
127.0.0.1:6379> LRANGE list 0 -1
1) "three"
2) "three"
3) "one"
127.0.0.1:6379> LREM list 1 three
(integer) 1

只保留指定范围的元素LTRIM(Ltrim)

127.0.0.1:6379> rpush mylist one two three four
(integer) 4
127.0.0.1:6379> LRANGE mylist 0 -1
1) "one"
2) "two"
3) "three"
4) "four"
127.0.0.1:6379> LTRIM mylist 1 2
OK
127.0.0.1:6379> LRANGE mylist 0 -1
1) "two"
2) "three"

RpopLpush

Lset替换指定下标的值

Linsert插入

sets

set的指令基本都是以S开头

127.0.0.1:6379> SADD myset hello # 添加值
(integer) 1
127.0.0.1:6379> SADD myset hello 
(integer) 0
127.0.0.1:6379> SADD myset name age sex
(integer) 3
127.0.0.1:6379> SMEMBERS myset # 查看所有的值
1) "name"
2) "hello"
3) "sex"
4) "age"
127.0.0.1:6379> SISMEMBER myset hello # 查看某个值是否在set里面,1为true,0为false
(integer) 1 
127.0.0.1:6379> SISMEMBER myset chen
(integer) 0

127.0.0.1:6379> SCARD myset # 查看set元素个数
(integer) 4
127.0.0.1:6379> SREM myset hello # 移除指定元素
(integer) 1
127.0.0.1:6379> SRANDMEMBER myset 1 # 随机抽出指定个数元素
1) "sex"
127.0.0.1:6379> SRANDMEMBER myset 2
1) "name"
2) "age"
127.0.0.1:6379> SPOP myset 1 # 随机删除一个元素
1) "age"
127.0.0.1:6379> SMEMBERS myset
1) "name"
2) "sex"

smove 将一个集合中的指定元素移动另一个元素中

127.0.0.1:6379> SADD set1 a b c d
(integer) 4
127.0.0.1:6379> sadd set2 c e
(integer) 2
127.0.0.1:6379> smove set1 set2 d
(integer) 1
127.0.0.1:6379> SMEMBERS set2
1) "c"
2) "e"
3) "d"

交并集合

127.0.0.1:6379> SDIFF set1 set2 # 差集
1) "a"
2) "b"
127.0.0.1:6379> SINTER set1 set2 # 交集
1) "c" 
127.0.0.1:6379> SUNION set1 set2 # 并集
1) "a"
2) "c"
3) "e"
4) "b"
5) "d"

Hashes

指令大多以H开头

127.0.0.1:6379> hset myhash field1 hello # 设置一个map
(integer) 1
127.0.0.1:6379> hmset myhash field2 redis field3 mongoDB # 设置多个map
(integer) 2
127.0.0.1:6379> HGETALL myhash # 查询所有的map
1) "field1"
2) "hello"
3) "field2"
4) "redis"
5) "field3"
6) "mongoDB"
127.0.0.1:6379> HGET myhash field1 #查询指定map
"hello"
127.0.0.1:6379> HMGET myhash field2 field3 # 查询多个map
1) "redis"
2) "mongoDB"

127.0.0.1:6379> HDEL myhash field1 # 删除指定的map(以map的key删除)
(integer) 1
127.0.0.1:6379> HLEN myhash # 获取hash的map数量
(integer) 2
127.0.0.1:6379> HEXISTS myhash field2 # 查看某个map是否存在
(integer) 1

127.0.0.1:6379> HKEYS myhash # 查看所有的key
1) "field2"
2) "field3"
127.0.0.1:6379> HVALS myhash
1) "redis"
2) "mongoDB"



用处:存储用户信息之类的,经常变动的信息。适合存储对象

127.0.0.1:6379> HSET user:1 name sen age 18 sex man
(integer) 3
127.0.0.1:6379> HGETALL user:1
1) "name"
2) "sen"
3) "age"
4) "18"
5) "sex"
6) "man"
Zsets(有序集合)

指令大多以Z开头

127.0.0.1:6379> ZADD salary 2500 xiaoming   # 中间的数字是score,根据这个score来排序
(integer) 1
127.0.0.1:6379> zadd salary 5000 xiaohong 1000 sen
(integer) 2
127.0.0.1:6379> ZRANGE salary 0 -1 # 正序
1) "sen"
2) "xiaoming"
3) "xiaohong"
127.0.0.1:6379> ZREVRANGE salary 0 -1 # 倒叙
1) "xiaohong"
2) "xiaoming"
3) "sen"

127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf 
1) "sen"
2) "xiaoming"
3) "xiaohong"
127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf withscores
1) "sen"
2) "1000"
3) "xiaoming"
4) "2500"
5) "xiaohong"
6) "5000"
127.0.0.1:6379> ZRANGEBYSCORE salary -inf 3000 withscores
1) "sen"
2) "1000"
3) "xiaoming"
4) "2500"
127.0.0.1:6379> ZCARD salary # 获取元素个数
(integer) 3
127.0.0.1:6379> ZCOUNT salary 1000 3000 # 获取指定区间范围的元素数量
(integer) 2

三种特殊数据类型

Geospatial地理空间

存储纬度经度相关信息

Hyperloglog

计数统计的算法

Bitmaps

位存储

只有两个状态的,都可以使用Bitmaps

事务

要么同时成功,要么同时失败,原子性!

Redis单条命令保持原子性,但是Redis事务不保证原子性!

Redis事物本质:一组命令的集合!一个事务中的所有命令都会被序列化,按照顺序执行(一次性,顺序性,排他性,没有原子性,没有隔离级别的概念!)

---------- 队列 set set set 队列 ----------

Redis事务的操作:

  • 开启事务(multi)
  • 命令入队
  • 执行事务(exec) 取消事务(discard)
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set name chen
QUEUED
127.0.0.1:6379> set age 18
QUEUED
127.0.0.1:6379> set sex man
QUEUED
127.0.0.1:6379> get name
QUEUED
127.0.0.1:6379> exec
1) OK
2) OK
3) OK
4) "chen"

如果是代码有问题,事务中所有命令都不会执行

如果是执行过程中发现一条命令有错(如让字符串+1),跳过这条命令,其他命令依旧执行(没有原子性)

监视(Watch)

悲观锁

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

乐观锁

很乐观,认为什么时候都不会出问题,所以不会上锁,更新数据的时候去判断一下,在此期间是否有人修改过这个数据

1.获取version

2.更新的时候比较version

Redis 使用watch监控变量当作乐观锁使用,如果编写事务过程中变量被其他线程修改了,那么当提交事务时会失败,需要unwatch这个变量,再watch变量,重写编写事务再提交

Jedis

1.导入依赖

 <dependencies>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>3.4.0</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.73</version>
        </dependency>
    </dependencies>

2.测试

package com.sen;

import redis.clients.jedis.Jedis;

public class TestPing {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("47.113.205.64", 6379);
        System.out.println(jedis.ping());
        String name = jedis.get("name");
        System.out.println(name);
    }
}

jedis的API和前面的指令一样

Spingboot整合

Springdata: JDBC、JPA、MongoDB、Redis

1.导入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

2.配置连接

spring.redis.port=6379
spring.redis.host=47.113.205.64
spring.redis.database=0

3.测试

@SpringBootTest
class RedisApplicationTests {

    @Autowired
    RedisTemplate redisTemplate;

    @Test
    void contextLoads() {
        redisTemplate.opsForValue().set("name", "chen");
        Object name = redisTemplate.opsForValue().get("name");
        System.out.println(name);
    }
}

自定义redisTemplate,添加序列化配置

@Configuration
public class RedisConfig {
     @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
         RedisTemplate<String, Object> template = new RedisTemplate<>();

         // Json序列化配置
         Jackson2JsonRedisSerializer objectJackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
         ObjectMapper om = new ObjectMapper();
         om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
         om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
         objectJackson2JsonRedisSerializer.setObjectMapper(om);
         // String的序列化
         StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
         
         
         template.setValueSerializer(objectJackson2JsonRedisSerializer);
         template.setHashValueSerializer(objectJackson2JsonRedisSerializer);
         template.setKeySerializer(stringRedisSerializer);
         template.setHashKeySerializer(stringRedisSerializer);
         
         template.afterPropertiesSet();
            
         template.setConnectionFactory(redisConnectionFactory);
         return template;
     }
}

Redis持久化(重点)

Redis是内存数据库

  • RDB
  • AOF

Redis实务操作

Redis订阅发布

先订阅

127.0.0.1:6379> SUBSCRIBE bilibili # 订阅一个频道
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "bilibili"
3) (integer) 1

后发布

127.0.0.1:6379> PUBLISH bilibili hello # 往指定频道发布消息
(integer) 1

订阅者自动接收到信息

127.0.0.1:6379> SUBSCRIBE bilibili
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "bilibili"
3) (integer) 1
1) "message"
2) "bilibili" # 来自哪个频道的消息
3) "hello"

原理:

  • 通过subscribe订阅某个频道后,redis-server里维护了一个字典,字典的key就是一个个频道,而字典的值是一条链表,链表中保存了所有订阅这个频道的所有用户。subscribe命令就是将用户添加到指定频道的链表中

  • 通过publish命令发布消息,redis-server使用给定的频道作为key,使用这个可以找到对应的链表,将消息发布给链表中的所有订阅者

使用场景:

  • 实时消息系统
  • 实时聊天(频道当作聊天室)
  • 订阅、关注系统

复杂的场景使用专业的MQ中间件做

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值