1. 前言

1.1 Reis是什么

Redis 是 C 语言开发的一个开源的(遵从 BSD 协议)高性能键值对(key-value)的   NoSQL的内存数据库,可以用作缓存、消息中间件等;具有以下特点:

1. 性能优秀,数据在内存中,读写速度非常快,支持并发 10W QPS;

2. 单进程单线程,是线程安全的,采用 IO 多路复用机制;

3. 丰富的数据类型,支持字符串(strings)、散列(hashes)、列表(lists)、集合(sets)、有序集合(sorted sets)等;

4. 支持数据持久化。可以将内存中数据保存在磁盘中,重启时加载;

5. 主从复制,哨兵,高可用;

可以用作分布式锁。可以作为消息中间件使用,支持发布订阅;

1.2 Redis支持的功能

1. 缓存: 提升数据的访问性能

2. 可用作低配版的消息中间件,支持发布订阅

3. 可用做分布式锁

4. 可以实现session共享

1.3 与memoryCache对比

对比内容

Redis

memoryCache
存储方式  Redis 支持持久化 Memcache不支持持久化,会把数据全部存在内存,很难解决缓存雪崩的问题
数据类型  Redis 支持五种数据类型 Memcache 对数据类型的支持简单,只支持简单的 key-value
底层模型

Redis 直接自己构建了 VM 机制

因为一般的系统调用系统函数的话

会浪费一定的时间去移动和请求

调用系统
Value大小 Redis 可以达到 1GB Memcache 只有 1MB

2. Redis内存管理

2.1 redisObject

Redis对象的类型、内部编码、内存回收、共享对象等功能,都需要redisObject支持

这样设计的好处:可以针对不同场景,对5种常用的数据类型设置多种不同的数据结构实现,从而优化对象在不同场景下的使用效率;

type: 代表一个 value 对象具体是何种数据类型。

encoding: 不同数据类型在 redis 内部的存储方式。

  比如:type=string 代表 value 存储的是一个普通字符串,那么对应的 encoding 可以是 raw 或者是 int。

  如果是 int 则代表实际 redis 内部是按数值型类存储和表示这个字符串的。

  当然前提是这个字符串本身可以用数值表示,比如:"123" "456"这样的字符串。

vm字段: 只有打开了 Redis 的虚拟内存功能,此字段才会真正的分配内存,该功能默认是关闭状态的。

Redis 使用 redisObject 来表示所有的 key/value 数据是比较浪费内存的

当然这些内存管理成本的付出主要也是为了给 Redis 不同数据类型提供一个统一的管理接口,实际作者也提供了多种方法帮助我们尽量节省内存使用。

2.2 基础命令

  服务端启动:redis-server redis.config

  客户端启动: redis-cli -p [服务器端口] -h [服务端ip]

1. 查看所有的key:keys *

2. 设置过期时间:set username nick EX 10  // ttl username 查看key剩余时间

3. 选择库:select 1 // redis有16个库0~15,可以选择不同的库存储数据,redis集群默认用0号库

4. 判断key是否存在:exits username

5. 删除key:del username

6. 查看key类型: type username

7. 数据持久化: save //保存在dump.rdb文件

8. 清空redis所有数据:flushall //flushdb为清空分库的数据,不删除dump.rdb

9. 单节点批量操作:mset/mget  //mset username nick mset address dg

10. 冷点数据设置超时清除:expire username 100 // expire为秒维度,pexpire为毫秒维度

11. 追加数据:如果 key 已经存在并且是一个字符串,append  key value

2.3 五种数据类型

  2.3.1 String

String类型的数据结构,是key-value形式,value为字符串/json串

1. 增(原生redis命令):set user1  {name:nick, age:15, gender:male}

2. 查(原生redis命令):get user1

3. 改(原生redis命令):set user1 {name:nick, age:, gender:male}

4. 删(原生redis命令):del user1

5. 记步器:incrby/decrby key //对value进行自增/自减,例如统计在线人数;

  2.3.2 Hash

Hash类型的数据结构是key-value形式,每一条value有是key-value形式

1. 增(原生redis命令):

  hset user1 name nick age 10 gender male //name age gender单条添加

  hmset user1 name nick age 10 gender male   //3个属性批量添加

  hincrby user1 age //age自增

2. 查(原生redis命令):

  hget user1 name //获取key为user1指向的name这个key指向的数据

  hmget user1 name age gender   //批量获取数据

     hgetall user1 // 获取key为user1指向的所有数据

  hkeys user1 // 获取key为user1指向的所有的hkey字段

  hlen user1 //获取key为user1指向的数据的个数

3.  改(原生redis命令):hset user1 name nick1

4. 删(原生redis命令):hdel user1 name

  2.3.3 List

List类型字符串列表,按照插入顺序排序。可以添加一个元素到列表的头部(左边)或者尾部(右边)

1. 增(原生redis命令):

  LPUSH key value [value ...]  // lpush nameList jack // 从左侧push

  LINSERT key BEFORE|AFTER pivot value   // linsert nameList before jerry tom//从左边,在jerty前面插入tom

  LTRIM key begin end // 保留链表范围内的数据元素

  rpoplpush key1 key2 // 原子级操作,两个链表数据交互,从key1删除,key2备份

2. 查(原生redis命令):

  LPOP key // lpop key  从链表中获取数据

  LRANGE key start stop // lrange nameList 0 -1 查看nameList中所有元素的个数

3. 改(原生redis命令):LSET key index value // lset nameList 1 nick2,修改nick->nick2

4. 删(原生redis命令):LREM key count value // lrem key count value 删除count个 数据为value的元素 count取0时,全部删除value

  2.3.4 Set

Set集合用来保存多个字符串元素,和list不用的是不允许有重复元素,并且无序

应用场景:用户的兴趣、爱好、关注人列表、粉丝列表等

1. 增(原生redis命令):

  SADD key member [member ...] // sadd nameList jack nick tom jerry

2. 查(原生redis命令):

  SRANDMEMBER key // srandmember nameList 随机获取一条数据,

  SMEMBERS key  //  smembers  nameList 查看nameList key的所有数据

3. 删(原生redis命令):

  SREM key member // srem nameList nick

4. 判断元素是否在集合中:

  SISMEMBER key member // sismember nameList jack

5. 不同集合间的交、并、差集

  SINTER key [key ...] // sinter nameList nameList2

  SUINON key [key ...] // sinter nameList nameList2

  SDIFF key [key ...] // sinter nameList nameList2  以第一个key为主

  2.3.5 ZSet

ZSet在Set集合的基础之上绑定了一个score作为排序的依据

1. 增(原生redis命令):

  ZADD key [NX|XX] [CH] [INCR] score member [score member ...]

  zadd result 100 jack  //100为score

  ZINCRBY key increment member // zincrby result 50 jack 增加jack成员的分数

2. 查(原生redis命令):

  ZCARD key // 查看key里面数据的个数

  ZSCORE key member // zscore result jack 查看成员的score

  ZRANGE key start stop [WITHSCORES]  // zrange result 0 -1 查看排名

  ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count] //根据score筛选

  ZRANK key member //计算成员的score排名

3. 删(原生redis命令):

  ZREM key member [member ...] // zrem result jack 删除某个成员

  ZREMRANGEBYRANK key start stop // 删除指定排名内的升序元素

  ZREMRANGEBYSCORE key min max // 删除score在[min, max]范围的元素

4. 不同集合间的交、并、差集

  ZINTERSTORE destination numkeys key [key ...]  //  zinterstore out 2 result result1,destination表示输出的ZSet  交集的score相加

3. Jedis客户端

3.1 redis配置文件

1. bind 127.0.0.1 ###指定redis只接收来自该IP地址的请求,如果不进行设置,将处理所有请求

2. protected-mode yes ###是否开启保护模式,默认开启;如果关闭,外部网络可以直接访问;如果开启,则需要bind ip或者设置访问密码(安全考虑)

3. port 6379  ###redis监听的端口

4. tcp-backlog 511 ###默认511,此参数标识确定了TCP连接中已完成队列(完成三次握手之后)的长度,可以理解为进程还没有accept的TCP连接的队列,tcp-backlog必须小于等于Linux系统定义的/proc/sys/net/somaxconn值

Linux的somaxconn默认为128,当系统并发量大,并且客户端速度缓慢的时候,可以将这两个参数一起参考设置,建议修改为2048,在/etc/sysctl.conf中添加 net.core.somaxconn=2048,然后终端执行sysctl -p命令生效

5. timeout 0 ###设置客户端空闲超过timeout时,redis服务端会断开连接,为0时表示服务端不会主动断开连接,此值不能小于0

6. tcp-keepalive 300 ###redis3.2以后默认为300秒,配置上这个参数之后,对于一些没有正常关闭的客户端,也可以及时关闭,连接超时受tcp-keepalive和另外三个参数影响:

tcp_keepalive_time default 7200 seconds

     tcp_keepalive_probes default 9

tcp_keepalive_intvl default 75 seconds

     连接超时公式为:tcp_keepalive_time+tcp_keepalive_intvl*tcp_keepalive_probes=7895s=131.58min  

7. daemonize yes

8. pidfile /var/run/redis/redis.pid ####redis的进程文件

9. loglevel notice ###服务端日志级别,包括debug(很多信息,方便开发、测试),verbose(许多有用的信息,但是没有debug级别信息多),notice(适当的日志级别,适合生产环境),warn(只有非常重要的信息)

3.2. redis客户端、服务端启动

基于上述配置文件的修改项:bind、port

1. 启动redis服务端

redis-server redis.config

2. 启动客户端

redis-cli redis-cli -h [host_ip]  -p  [port]

3. 停止redis服务端

登录客户端执行:redis-cli -h [host_ip] -p [port] shutdown

3.3 Jedis代码实践

  3.3.1 Jedis连接池连接

1. 添加maven依赖

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-redis</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
</dependencies>

2. application.properties配置

spring.redis.sharedpool.nodes=192.168.75.*:6739,192.168.75.*:6780,192.168.75.*:6381
spring.redis.sharedpool.maxTotal=200
spring.redis.sharedpool.maxIdle=10
spring.redis.sharedpool.minIdle=2

3. springboot管理redis连接池

@Configuration
@ConfigurationProperties("spring.redis.sharedpool")
@Setter
@Getter
public class SharedJedisConfigPool { private List<String> nodes; //[192.168.75.132:6739,192.168.75.132:6780,192.168.75.132:6381]
private Integer maxTotal;
private Integer maxIdle;
private Integer minIdle; @Bean
public ShardedJedisPool sharedPoolInit() {
List<JedisShardInfo> infoList = new ArrayList<>();
for (String node : nodes) {
String ip = node.split(":")[0];
int port = Integer.parseInt(node.split(":")[1]);
infoList.add(new JedisShardInfo(ip, port));
}
GenericObjectPoolConfig config = new GenericObjectPoolConfig();
config.setMaxTotal(maxTotal);
config.setMaxIdle(maxIdle);
config.setMinIdle(minIdle);
return new ShardedJedisPool(config, infoList);
}
}

4. 连接池测试

class DemoApplicationTests {
@Autowired
private ShardedJedisPool pool;
@Test
public void func4() {
ShardedJedis jedis = pool.getResource();
jedis.set("user", "nick");
}
}

  3.3.2 redis缓存逻辑

class DemoApplicationTests {
@Autowired
private ShardedJedisPool pool;
@Test
public void func1() {
String key = "04f2c34c";
ShardedJedis jedis = pool.getResource();
if (jedis.exists(key)) { //redis缓存有数据
String value = jedis.get(key);
} else {
String key2 = "04f2c34c";//查询数据库
jedis.set(key2, "nick");
}
}
}

  3.3.3 SharedJedis分片对象

1. 旧版redis中通常使用客户端分片来解决水平扩容问题,即启动多个redis服务端,客户端决定key交由哪个节点存储,下次客户端直接到该节点读取key

2. 可以实现将整个数据分布存储在N个数据库节点中,每个节点只存放总数据量的1/N。

3. 对于需要扩容的场景来说,在客户端分片后,如果想增加节点,需要对数据进行手工迁移,在迁移时为了保证数据一致性,需要将集群暂时下线,相对比较复杂

自定义简单版分片规则如下:

n个redis服务端几乎均匀存储,hash取余,散列,只要有散列就会有数据倾斜

key.hashCode()结果可正可负

key.hashCode()&Integer.MAX_VALUE结果一定为正数,key和结果唯一对应

key.hashCode()&Integer.MAX_VALUE%n

4. 分片计算

4.1 自定义分片

如3.3.3小节讨论,在使用redis集群时,可以通过自定义的简单版的hash散列方式分片,但是有以下两个缺点,因此引出hash一致性算法

1. 数据倾斜不可避免,只要有hash散列,就一定会有数据倾斜,不可能完全均匀;

2. 扩容、缩容时,数据迁移量巨大★

4.2 hash一致性

SharedJedis实现了分片底层的算法——hash一致性(解决了取余算法再扩缩容时,数据迁移量过大的问题);为什么?redis的hash逻辑为:将所有节点信息hash散列到0~2^32-1区间

4.2 hash一致性问题

引入问题:

1. 数据倾斜:

如果节点的数量很少,而hash环空间很大( 0 ~ 2^32),直接进行一致性hash会导致节点在环上的位置会很不均匀,挤在某个很小的区域。最终对分布式集群的每个实例上储存的缓存数据量不一致,会发生严重的数据倾斜;

2. 缓存雪崩

如果每个节点在环上只有一个节点,那么可以想象,当某一集群从环中消失时,它原本所负责的任务将全部交由顺时针方向的下一个集群处理。

例如当6379退出时,它原本所负责的缓存将全部交给6380处理。这就意味着6380的访问压力会瞬间增大。如果6380因为压力过大而崩溃,那么更大的压力又会向6381压过去,最终服务压力就像滚雪球一样越滚越大,最终导致雪崩

引入虚拟节点

解决上述两个问题最好的办法就是扩展整个环上的节点数量,因此我们引入了虚拟节点的概念。一个实际节点将会映射多个虚拟节点,多次hash,这样Hash环上的空间分割就会变得均匀。

5. 高可用

5.1 主从复制

在同一台虚拟机上构建1主3从的主从模式,一个主节点一般最多6个从节点;

0. 主节点redis配置文件bind要绑定0.0.0.0表示所有连接都可以访问,没有绑定的话,不可以访问主;

1. 创建从节点redis启动配置文件,redis02.config、redis03.config,并修改端口为6380、6381

2. 启动3个redis-server实例 redis-server redis.config、redis-server redis02.config、redis-server redis03.config

3. 主从关系创建,规定6379节点master、6380节点作为slave 、6381节点作为slave;

4. 登录查看节点状态的命令,以客户端登录6379节点为例:6379> info replication

4. 通过配置文件定义主从关系

5. 通过命令挂在主节点,命令如下:从节点> slaveof masterip masterport

6. 本处通过命令方式指定主从关系,客户端登录6080、6081服务端,执行第五步命令

7. 查看6379、6381、6382节点的replicatinon信息,

  5.1.1 主从关系测试

测试过程:

主节点6379写数据,从节点6380读数据

1. 登录主节点客户端 set name nick

2. 登录从节点客户端 get name

3. 登录从节点 flushal   执行失败

4. 主节点shutdown

测试结果:

从节点配置默认为只读模式read-only,主节点宕机,从节点不会自动变为主节点,从节点info replication状态变为如下:

master_link_status:down
master_last_io_seconds_ago:-1

5.2 哨兵模式

  5.2.1 什么是哨兵

redis-cluster(redis 3.0)出现之前,redis的使用几乎都是围绕哨兵模式展开,哨兵过程如下:

1. 起单独线程(特殊的redis-cluster)开启对主从结构的监听

2. 监听主,从主节点调用命令info replication,获取主从结构的所有信息,保存在内存,每1秒钟发起一次心跳检测(RPC远程通信协议),一旦主节点宕机,哨兵集群发起投票选举,过半票数确定结果,增加可信;

3. 哨兵容忍度:允许宕机的个数是哨兵的容忍度,为了保障多票选取,5个哨兵集群节点的容忍度为2,6个哨兵集群的容忍度为2;2n-1个集群和2n个集群的容忍度是一样的,因此哨兵集群都是奇数个(少用一个资源)

  5.2.2 搭建哨兵集群

模式:3个哨兵节点,管理6379,6380,6382主从结构,过程如下:

1. 修改哨兵的模板配置文件(sentinel.conf),配置3个哨兵端口:26379,26380,26381

2. 释放保护模式注释,修改为no

3. 修改监听主节点的配置核心内容:sentine monitor mymaster [master_ip] [master_port]  [num]

3..1 mymaster是自定义名称 标识当前哨兵监听的主从结构的代号,多个哨兵监听同一个主从结构的话,此处要保持一致

  3.2 num表示为主观下限票数,当哨兵集群不断宕机时,最少要剩下的节点数量,和宕机容忍度有关

4. 拷贝三份,分别做如上修改

5. 启动哨兵(启动之前确定主从集群正常):redis-sentinel sentine.conf

6. 哨兵没有restart等操作

7. kill掉主节点,查看哨兵日志

8. 哨兵发现主节点宕机,开始投票,查看日志num,确定宕机,投票选取新的节点作为主节点,主节点一旦恢复,则会也从节点角色加入集群,继续提供服务

9. 重启哨兵日志时,删除尾部日志,避免影响本次启动

  5.2.2 Jedis连接哨兵集群

1. application.properties配置

spring.redis.sentinel=192.168.75.132:26379,192.168.75.132:26380,192.168.75.132:26381
spring.redis.sharedpool.maxTotal=200
spring.redis.sharedpool.maxIdle=10
spring.redis.sharedpool.minIdle=2

2. springboot管理sentinel连接池

@Configuration
@ConfigurationProperties("spring.redis.sentinel")
@Getter
@Setter
public class SentinelConfig { private List<String> nodes;
private Integer maxTotal;
private Integer maxIdle;
private Integer minIdle; @Bean
public JedisSentinelPool sentinelPoolInit() {
//搜集哨兵集群信息
Set<String> sentinelSet = new HashSet<>();
for (String node : nodes) {
String ip = node.split(":")[0];
int port = Integer.parseInt(node.split(":")[1]);
sentinelSet.add(new HostAndPort(ip, port).toString());
}
//连接池配置对象
GenericObjectPoolConfig config = new GenericObjectPoolConfig();
config.setMaxTotal(maxTotal);
config.setMaxIdle(maxIdle);
config.setMinIdle(minIdle);
//构建哨兵管理对象
return new JedisSentinelPool("mymaster", sentinelSet, config);
}
}

3. 连接池测试

@SpringBootTest
class DemoApplicationTests {
@Autowired
private JedisSentinelPool sentinelPool; @Test
public void func5() {
HostAndPort masterIP = sentinelPool.getCurrentHostMaster();
Jedis jedis = sentinelPool.getResource(); //哨兵模式只能操作主节点
String name = jedis.get("name");
}
}

5.3. redis-cluster★

  5.3.1 什么是redis集群模式

  5.3.2 构建redis集群

【初识Redis】的更多相关文章

  1. 初识redis数据类型

    初识redis数据类型 1.String(字符串) string是redis最基本的类型,一个key对应一个value. string类型是二进制安全的.意思是redis的string可以包含任何数据 ...

  2. Redis——学习之路三(初识redis config配置)

    我们先看看config 默认情况下系统是怎么配置的.在命令行中输入 config get *(如图) 默认情况下有61配置信息,每一个命令占两行,第一行为配置名称信息,第二行为配置的具体信息.     ...

  3. Redis——学习之路二(初识redis服务器命令)

    上一章我们已经知道了如果启动redis服务器,现在我们来学习一下,以及如何用客户端连接服务器.接下来我们来学习一下查看操作服务器的命令. 服务器命令: 1.info——当前redis服务器信息   s ...

  4. 01:初识Redis

    付磊和张益军两位大咖写的葵花宝典(Redis开发和运维)学习笔记. 一.初识Redis 1.redis简介 Redis是一种基于键值对(key-value)的NoSQL数据库,与很多键值对数据库不同的 ...

  5. redis实战笔记(1)-第1章 初识Redis

    第1章 初识Redis 注:本书在redis3.0版本的,比如redis3.0以后支持服务端集群.3.0之前只能客户端分片.    本章主要内容 1.Redis与其他软件的相同之处和不同之处 2.Re ...

  6. Linux(5)- MariaDB、mysql主从复制、初识redis

    一.MYSQL(mariadb) MariaDB数据库管理系统是MySQL的一个分支,主要由开源社区在维护,采用GPL授权许可. 开发这个分支的原因之一是:甲骨文公司收购了MySQL后,有将MySQL ...

  7. 分布式数据存储 之 Redis(一) —— 初识Redis

    分布式数据存储 之 Redis(一) -- 初识Redis 为什么要学习并运用Redis?Redis有什么好处?我们步入Redis的海洋,初识Redis. 一.Redis是什么 ​ Redis 是一个 ...

  8. [转]Redis之(一)初识Redis

    原文地址:http://blog.csdn.net/u012152619/article/details/52550315 Redis之(一)初识Redis 标签: Redisredis-server ...

  9. 1.初识Redis

    作者 微信:tangy8080 电子邮箱:914661180@qq.com 更新时间:2019-08-14 20:35:36 星期三 欢迎您订阅和分享我的订阅号,订阅号内会不定期分享一些我自己学习过程 ...

  10. Redis——学习之路一(初识redis)

    在接下来的一段时间里面我要将自己学习的redis整理一遍,下面是我整理的一些资料: Redis是一款依据BSD开源协议发行的高性能Key-Value存储系统(cache and store),所以re ...

随机推荐

  1. 前端基础知识之html和css全解

    前端回顾 目录 前端回顾 基础知识 HTTP协议 认识HTML HTML组成 HTML标签 div和span标签 特殊的属性 常用标签 认识css 选择器 属性 前端就是展示给用户并且与用户进行交互的 ...

  2. Java实现 蓝桥杯 算法提高 抽卡游戏

    试题 算法提高 抽卡游戏 某个抽卡游戏卡池抽出限定卡的概率为p,该游戏有一个"井"的机制,抽满k次卡后直接送这张限定卡.试求获得这张限定卡需要的期望抽卡次数.输入为一行,用空格隔开 ...

  3. Java 第十一届 蓝桥杯 省模拟赛 小明的城堡

    小明用积木搭了一个城堡. 为了方便,小明在搭的时候用的是一样大小的正方体积本,搭在了一个 n 行 m 列的方格图上,每个积木正好占据方格图的一个小方格. 当然,小明的城堡并不是平面的,而是立体的.小明 ...

  4. Java实现 LeetCode 680 验证回文字符串 Ⅱ(暴力)

    680. 验证回文字符串 Ⅱ 给定一个非空字符串 s,最多删除一个字符.判断是否能成为回文字符串. 示例 1: 输入: "aba" 输出: True 示例 2: 输入: " ...

  5. Java实现 蓝桥杯VIP 算法训练 整除问题

    问题描述 编写一个程序,输入三个正整数min.max和factor,然后对于min到max之间的每一个整数(包括min和max),如果它能被factor整除,就把它打印出来. 输入格式:输入只有一行, ...

  6. Java实现 LeetCode 171 Excel表列序号

    171. Excel表列序号 给定一个Excel表格中的列名称,返回其相应的列序号. 例如, A -> 1 B -> 2 C -> 3 ... Z -> 26 AA -> ...

  7. Java实现 蓝桥杯VIP 算法提高 三角形面积

    算法提高 三角形面积 时间限制:1.0s 内存限制:256.0MB 问题描述 由三角形的三边长,求其面积. 提示:由三角形的三边a,b,c求面积可以用如下的公式: s=(a+b+c)/2 输入格式 由 ...

  8. PAT 统计同成绩学生

    本题要求读入 N 名学生的成绩,将获得某一给定分数的学生人数输出. 输入格式: 输入在第 1 行给出不超过 105 的正整数 N,即学生总人数.随后一行给出 N 名学生的百分制整数成绩,中间以空格分隔 ...

  9. js动态绑定class(当前父级div下的子元素有没有这个class,有的话移除,没有的话添加)

    <div class="layui-inline" id=‘’   onclick="changeType(id)">                ...

  10. 利用tcpdump命令统计http的GET和POST请求

    1.搭建的知识库服务器, 需要统计来访者都是哪些人,因为系统不是自己开发的,看不到访问日志.所以考虑从系统层面抓取访问流量来实现. 2.通过tcpdump抓取的数据包,在wireshark中打开发现, ...