Redis学习笔记——快速入门篇

redis快速入门

  • 1.redis概述
  • 2.redis常识问题
  • 3.五大数据类型
    • 3.1String类型
    • 3.2列表list
    • 3.3无序集合Set
    • 3.4有序集合Zset
    • 3.5哈希表Hash
  • 4.读懂redis的配置文件redis.conf
  • 5.redis的事务
    • 5.1理论
  • 6.Java与redis整合
    • 6.1Jedis
    • 6.2SpringBoot整合redis

1.redis概述

Redis:REmote DIctionary Server(远程字典服务器)

是完全开源免费的,用C语言编写的,遵守BSD协议,是一个高性能的(Key/Value)分布式内存数据库,基于内存运行,并支持持久化的NoSQL数据库,是当前最热门的NoSQL数据库之一,也被人们称为数据结构服务器。

Redis与其他key-value缓存产品有以下三个特点

  • Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用。
  • Redis不仅仅支持简单的 key-value 类型的数据,同时还提供list、set、zset、hash等数据结构的存储。
  • Redis支持数据的备份,即master-slave模式的数据备份。

2.redis常识问题

redis默认16个数据库,类似数组下标从零开始,初始默认使用零号库。

在redis的配置文件redis.conf中,可以查看数据库的默认配置!!!

Select命令切换数据库

select 7 #切换到第七个数据库

Dbsize查看当前数据库的key的数量

DBSIZE

Flushdb:清空当前库
Flushall:清空全部的库

关于Key的一些操作

# 查看所有的key
keys * 
127.0.0.1:6379> keys *
(empty list or set)
127.0.0.1:6379> set name haha
OK
127.0.0.1:6379> keys *
1) "name"
# exists key 的名字,判断某个key是否存在
127.0.0.1:6379> EXISTS name
(integer) 1
127.0.0.1:6379> EXISTS name1
(integer) 0
# 移除当前库中的key
move key db ---> 当前库就没有了,被移除了
127.0.0.1:6379> move name 1
(integer) 1
127.0.0.1:6379> keys *
(empty list or set)
# 为给定 key 设置生存时间,当 key 过期时(生存时间为 0 ),它会被自动删除。
expire key 秒钟:
# ttl key 查看还有多少秒过期,-1 表示永不过期,-2 表示已过期
127.0.0.1:6379> set name haha
OK

常用命令说明:
127.0.0.1:6379> EXPIRE name 10
(integer) 1
127.0.0.1:6379> ttl name
(integer) 4
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) 1
127.0.0.1:6379> ttl name
(integer) -2
127.0.0.1:6379> keys *
(empty list or set)
# 查看你的key是什么类型
type key
127.0.0.1:6379> set name haha
OK
127.0.0.1:6379> get name
"haha"
127.0.0.1:6379> type name
string

更多redis命令

为什么redis是单线程

redis 6 之前的版本是单进程、单线程的,6版本之后支持多线程!!!

redis很快,是因为redis是基于内存的操作,CPU不是redis的瓶颈,redis
的瓶颈最有可能是机器内存的大小或者网络带宽!!!

先说两个误区:一、高性能的服务器不一定都是多进程、多线程的;二、多线程不一定都不比单线程快,比如单核机器。

在我们通常的认知中,高性能都是通过多进程、多线程实现的。比如Nginx是多进程单线程的,Memcached是单进程多线程的。

在计算机的世界中,CPU的速度是远大于内存的速度的,同时内存的速度也是远大于硬盘的速度。redis的操作都是基于内存的,绝大部分请求是纯粹的内存操作,非常迅速,使用单线程可以省去多线程时CPU上下文会切换的时间,也不用去考虑各种锁的问题,不存在加锁释放锁操作,没有死锁问题导致的性能消耗。对于内存系统来说,多次读写都是在一个CPU上,没有上下文切换效率就是最高的!既然单线程容易实现,而且 CPU 不会成为瓶颈,那就顺理成章的采用单线程的方案了(毕竟采用多线程会有很多麻烦)。

3.五大数据类型

redis的用处

  • 作为数据库存储数据
  • 缓存
  • 消息中间件MQ

五种数据结构

  • String:字符串类型
  • List:列表类型
  • Set:无序集合类型
  • ZSet:有序集合类型
  • Hash:哈希表类型

本篇笔记仅对redis的基本操作进行总结,关于底层的实现,详见后续学习笔记!!!

3.1String类型

单值单value

常用操作、增删查

127.0.0.1:6379> set hello redis
OK
127.0.0.1:6379> get hello
"redis"
127.0.0.1:6379> del hello
(integer) 1
127.0.0.1:6379> get hello
(nil)

append、strlen

# hello不存在
127.0.0.1:6379> exists hello
(integer) 0
# # 对不存在的key进行APPEND,等同于 SET 语句
127.0.0.1:6379> append hello redis2
(integer) 6
127.0.0.1:6379> get hello
"redis2"
# 对已存在的key进行APPEND,直接在string后加内容
127.0.0.1:6379> append hello redis3
(integer) 12
127.0.0.1:6379> get hello
"redis2redis3"
127.0.0.1:6379> strlen hello
(integer) 12

incr、decr 加1、减1
incrby、decrby 加上、减去指定的增量值

String类型的value还可以存储数字

127.0.0.1:6379> set views 0
OK
127.0.0.1:6379> get views
"0"
127.0.0.1:6379> incr views
(integer) 1
127.0.0.1:6379> incr views
(integer) 2
127.0.0.1:6379> decr views
(integer) 1
127.0.0.1:6379> get views
"1"

3.2列表list

lpush、rpush 添加list数据

#lpush:将一个或多个值插入到列表头部。(左)
#rpush:将一个或多个值插入到列表尾部。(右)
lpush list-key item
rpush list-key item

lrange、lindex 获取数据

#lrange:返回列表中指定区间内的元素,区间以偏移量START和STOP指定
lrange list-key start stop
#lindex:返回index位置的元素
lindex list-key 

lpop、rpop 删除list中的数据

#lpop 命令用于移除并返回列表的第一个元素。当列表 key 不存在时,返回nil 。
lpop list-key
#rpop 移除列表的最后一个元素,返回值为移除的元素。
rpop list-key

应用场景:
Redis list的应用场景非常多,也是Redis最重要的数据结构之一。我们可以轻松地实现最新消息排行等功能。Lists的另一个应用就是消息队列,可以利用Lists的PUSH操作,将任务存在Lists中,然后工作线程再用POP操作将任务取出进行执行。

3.3无序集合Set

单值多value

sadd 添加元素

# sadd 将一个或多个成员元素加入到集合中,不能重复
127.0.0.1:6379> sadd set a,b
(integer) 1
127.0.0.1:6379> sadd set c
(integer) 1
127.0.0.1:6379> sadd set d,eee,ff
(integer) 1

smembers 获取数据

# smembers 返回集合中的所有的成员。
127.0.0.1:6379> smembers set
1) "d,eee,ff"
2) "a,b"
3) "c"

sismember 判断成员元素是否是集合的成员。

127.0.0.1:6379> smembers set
1) "d,eee,ff"
2) "a,b"
3) "c"
127.0.0.1:6379> sismember set c
(integer) 1
127.0.0.1:6379> sismember set a
(integer) 0

应用场景:
Redis为集合提供了求交集、并集、差集等操作,故可以用来求共同好友等操作。

3.4有序集合Zset

zadd 添加数据

# 将一个或多个成员元素及其分数值加入到有序集当中。
127.0.0.1:6379> zadd zset 3 haha
(integer) 1
127.0.0.1:6379> zadd zset 1 ming 2 hong
(integer) 2

zrange 获取数据

# 返回有序集中,指定区间内的成员
127.0.0.1:6379> zrange zset 0 1
1) "ming"
2) "hong"
127.0.0.1:6379> zrange zset 0 -1
1) "ming"
2) "hong"
3) "haha"

zrangebyscore 返回分数score之间的数据

# 返回有序集合中指定分数区间的成员列表。有序集成员按分数值递增(从小到大)
次序排列。
127.0.0.1:6379> zrangebyscore zset 0 1
1) "ming"
127.0.0.1:6379> zrangebyscore zset 1 3
1) "ming"
2) "hong"
3) "haha"

zrem 删除数据

# 移除有序集中的一个或多个成员
127.0.0.1:6379> zrem zset hong
(integer) 1

应用场景
以某个条件为权重,比如按顶的次数排序,ZREVRANGE命令可以用来按照得分来获取前100名的用户,ZRANK可以用来获取用户排名,非常直接而且操作容易。Redis sorted set的使用场景与set类似,区别是set不是自动有序的,而sorted set可以通过用户额外提供一个优先级(score)的参数来为成员排序,并且是插入有序的,即自动排序。

3.5哈希表Hash

kv模式不变,但V是一个键值对

hset 添加元素

# hset key field value
127.0.0.1:6379> hset hash hash1 value1
(integer) 1
127.0.0.1:6379> hset hash hash1 value2
(integer) 0
127.0.0.1:6379> hset hash hash2 value2 hash3 value3
(integer) 2

hget 、hgetall 获取元素

# hget key field
127.0.0.1:6379> hget hash hash1
"value1"
# hgetall key
127.0.0.1:6379> hgetall hash
1) "hash1"
2) "value2"
3) "hash2"
4) "value2"
5) "hash3"
6) "value3"

hmset、hmget 对多个field-value对进行操作

# 同时将多个field-value对设置到哈希表中。会覆盖哈希表中已存在的字段。
127.0.0.1:6379> hmset mhash hash1 value1 hash2 value2
OK
127.0.0.1:6379> hmget mhash hash1 hash2
1) "value1"
2) "value2"

hdel 删除数据

# 用于删除哈希表key中的一个或多个指定字段
127.0.0.1:6379> hdel hash hash1 hash2
(integer) 2
127.0.0.1:6379> hgetall hash
1) "hash3"
2) "value3"

应用场景
Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。存储部分变更的数据,如用户信息等。

4.读懂redis的配置文件redis.conf

配置文件开头就说了redis-server的使用

Redis学习笔记——快速入门篇_第1张图片
Redis启动的时候,通过配置文件来启动
命令:redis-server /usr/local/bin/myredis/redis.conf(配置文件的路径)

单位 Units

Redis学习笔记——快速入门篇_第2张图片

  • 配置大小单位,开头定义了一些基本的度量单位,只支持bytes,不支持bit
  • 对大小写不敏感

includes 可以包含其他配置文件

Redis学习笔记——快速入门篇_第3张图片

network

bind 127.0.0.1 # 绑定的ip
protected-mode yes # 保护模式,是否受保护,默认开启
port 6379 # 默认端口

general 通用配置

# 默认情况下,Redis不作为守护进程运行。需要开启的话,改为 yes
daemonize yes 
# 可通过upstart和systemd管理Redis守护进程
supervised no 
# 以后台进程方式运行redis,则需要指定pid 文件
pidfile /var/run/redis_6379.pid 
# 日志级别。可选项有:
# debug(记录大量日志信息,适用于开发、测试阶段);
# verbose(较多日志信息);
# notice(适量日志信息,使用于生产环境);
# warning(仅有部分重要、关键信息才会被记录)。
loglevel notice
# 日志文件的位置,当指定为空字符串时,为标准输出
logfile "" 
# 设置数据库的数目。默认的数据库是DB 0
databases 16 
# 是否总是显示logo,开启redis服务那个logo
always-show-logo yes 

快照

什么是快照呢?快照是实现持久化的一种方法。快照,可以理解为拍照一样,把整个内存数据映射到硬盘中,保存一份到硬盘,因此恢复数据起来比较快,把数据映射回去即可!!!

# 900秒(15分钟)内至少1个key值改变(则进行数据库保存--持久化)
save 900 1
# 300秒(5分钟)内至少10个key值改变(则进行数据库保存--持久化)
save 300 10
# 60秒(1分钟)内至少10000个key值改变(则进行数据库保存--持久化)
save 60 10000
stop-writes-on-bgsave-error yes # 持久化出现错误后,是否依然进行继续进行工作
rdbcompression yes # 使用压缩rdb文件 yes:压缩,但是需要一些cpu的消耗。no:不压
缩,需要更多的磁盘空间
rdbchecksum yes # 是否校验rdb文件,更有利于文件的容错性,但是在保存rdb文件的时候,会有大概10%的性能损耗
dbfilename dump.rdb # dbfilenamerdb文件名称
dir ./ # dir 数据目录,数据库的写入会在这个目录。rdb、aof文件也会写在这个目录

注:rdb文件是持久化的文件(快照文件),它保存了redis 在某个时间点上的数据集。 这种文件非常适合用于进行备份和灾难恢复。

security 安全

访问密码的查看,设置和取消

# 启动redis
# 连接客户端
# 获得和设置密码
config get requirepass
config set requirepass "123456"
#测试ping,发现需要验证
127.0.0.1:6379> ping
NOAUTH Authentication required.
# 验证
127.0.0.1:6379> auth 123456
OK
127.0.0.1:6379> ping
PONG

客户端的一些限制

maxclients 10000 # 设置能连上redis的最大客户端连接数量
maxmemory  # redis配置的最大内存容量
maxmemory-policy noeviction # maxmemory-policy 内存达到上限的处理策略

6种处理策略

  • volatile-lru:利用LRU算法移除设置过期时间的key。
  • volatile-random:随机移除设置过期时间的key。
  • volatile-ttl:移除即将过期的key,根据最近过期时间来删除(辅以TTL)
  • allkeys-lru:利用LRU算法移除任何key。
  • allkeys-random:随机移除任何key。
  • noeviction:不移除任何key,只是返回一个写错误

appendonly 模式

默认使用rdb持久化方式,不开启aof模式

appendonly no # 是否以append only模式作为持久化方式
appendfilename "appendonly.aof" # appendfilename AOF 文件名称
appendfsync everysec # appendfsync aof持久化策略的配置

AOF持久化配置策略

  • no表示不执行fsync,由操作系统保证数据同步到磁盘,速度最快。
  • always表示每次写入都执行fsync,以保证数据同步到磁盘。
  • everysec表示每秒执行一次fsync,可能会导致丢失这1s数据。

5.redis的事务

5.1理论

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

Redis 事务的本质是一组命令的集合。事务支持一次执行多个命令,一个事务中所有命令都会被序列化。在事务执行过程,会按照顺序串行化执行队列中的命令,其他客户端提交的命令请求不会插入到事务执行命令序列中。
总结说:redis事务就是一次性顺序性排他性的执行一个队列中的一系列命令。

Redis事务没有隔离级别的概念:

批量操作在发送 EXEC 命令前被放入队列缓存,并不会被实际执行!

Redis不保证原子性:

Redis中,单条命令是原子性执行的,但事务不保证原子性,且没有回滚。事务中任意命令执行失败,其余的命令仍会被执行。

Redis事务的三个阶段:

  • 开始事务multi
  • 命令入队
  • 执行事务exec

Redis事务相关命令如下:

watch key1 key2 ... #监视一个或多个key,如果在事务执行之前,被监视的key被其他命令改动,则事务被打断 ( 类似乐观锁 )
multi # 标记一个事务块的开始( queued )
exec # 执行所有事务块的命令 ( 一旦执行exec后,之前加的监控锁都会被取消掉 )
discard # 取消事务,放弃事务块中的所有命令
unwatch # 取消watch对所有key的监控

Watch命令详解:

watch监控指令类似于乐观锁,在事务提交时,如果watch监控的多个KEY中任何KEY的值已经被其他客户端更改,则使用EXEC执行事务时,事务队列将不会被执行,同时返回Nullmulti-bulk应答以通知调用者事务执行失败。

测试watch命令的使用!!!

转账,A转给B100元,A有1000,B有0

正常执行成功!

127.0.0.1:6379> set A 1000
OK
127.0.0.1:6379> set B 0
OK
127.0.0.1:6379> watch A
OK
#事务正常结束,数据期间没有发生变动,这个时候就正常执行成功!!!
127.0.0.1:6379> multi  
OK
127.0.0.1:6379> decrby A 100
QUEUED
127.0.0.1:6379> incrby B 100
QUEUED
127.0.0.1:6379> exec
1) (integer) 900
2) (integer) 100

场景:小明去给账户充钱(A),充到1000;小红用账户的钱(A)给小李转账(B)100;俩人同时进行!!!

127.0.0.1:6379> get A
"900"
127.0.0.1:6379> set A 1000
OK
127.0.0.1:6379> watch A # 加锁
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby A 100
QUEUED
127.0.0.1:6379> incrby B 100
QUEUED
127.0.0.1:6379> exec
(nil)

在小红转账期间,加上watch乐观锁,小明给账户充钱了,小红转账失败!!!

:一但执行 EXEC 开启事务的执行后,无论事务使用执行成功, WARCH 对变量的监控都将被取消。
故当事务执行失败后,需重新执行WATCH命令对变量进行监控,并开启新的事务进行操作。

6.Java与redis整合

6.1Jedis

Jedis是Redis官方推荐的Java连接开发工具!!!

API与Linux的redis命令一致,不重复了。

6.2SpringBoot整合redis

在SpringBoot2.x之后,jedis被替换为了lettuce
jedis:采用的直连,多个线程操作的话,是不安全的,如果想要避免不安全,使用jedis pool连接池!更像BIO模式!!!
lettuce:采用netty,实例可以再多个线程种进行共享,不存在线程不安全的情况,可以减少线程数据,更像NIO模式!!!

在SpringBoot中一般使用RedisTemplate提供的方法来操作Redis。

导入pom依赖

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

配置文件properties或者yaml

spring:
	redis:
		host: 127.0.0.1
		port: 6379
		password: 123456
		lettuce:
			shutdown-timeout: 0ms

使用RedisTemplate来操作

@SpringBootTest
class SpringdataRedisApplicationTests {
    @Autowired
    private RedisTemplate redisTemplate;

    @Test
    void contextLoads() {
        //操作String类型
        redisTemplate.opsForValue().set("hello","redis");
        System.out.println(redisTemplate.opsForValue().get("hello"));
    }
}

当然,RedisTemplate也可以根据需求自己封装,命名为RedisTemplate即可!!!

你可能感兴趣的:(redis,redis)