大数据 互联网架构阶段 Redis
Redis
零、 目录
- 高并发思路
- 电商网站中缓存数据库的设计
- 缓存介绍
- 按照redis
- redis常用命令
- redis其他数据结构
- 数据分布式存储
- Jedis客户端
- 哈希一致性
- 补充
一、 高并发思路
- 技术: tomcat集群+ nginx
- 理论上引入20台nginx — 100万/s的并发量
- 问题: 当100万个用户同时增删该查时 , 就出现了系统瓶颈
- 系统瓶颈: 后台程序能支持100万/秒的访问 , 但是数据库扛不住100万/s的访问
- redis
- 分布式 、 nosql 、 可以持久化+内存 、 内存 、 数据库
- 分布式: 数据库被划分
- nosql(not only sequence query language): 不仅仅支持关系型 、 结构化的数据库 , 而且支持非关系型 、 非结构化数据
- 可持久化+内存: 启动恢复机制(redis启动之后立即恢复之前的数据)
- 数据库: 数据存储
- 作用: 可以分布式的存储海量的数据 , 放到内存中 , 可以做缓存数据库
二、 电商网站中缓存数据库如何设计
- 缓存可以如何添加
- 数据库缓存:
- 执行的过程包括sql和组织查询结果 , 根据sql可以创建缓存 , 存储已经查过的resultSet , 节省了资源调度重组resultSet
- 持久层缓存:
- 减少数据库获取的结果转化为对象的过程 , 缓存直接调用保存的对象结果
- 业务层缓存:减少调用层次
- 控制层缓存: 减少调用层次
- 数据库缓存:
- 问题: 可以不可以过多的使用缓存?
- 缓存是占用内存空间的 ,过多的缓存插入 , 容易造成数据的冗余 , 在内存不够时 , 清空逻辑会交叉导致数据库失效
- 结论: 缓存引入的最终目的:
- 减少数据库的访问压力
- 减少网路传输
- 减少封装层次
三、缓存介绍
- 主流的缓存结构:
- ehcache(很多数据库底层缓存用的就是ehcache)并发量差
- memoryCache , 10年前 , 并发量高(100万左右/秒) , 缺点: 不落地(数据不能持久化 , 在宕机后不能立即恢复丢失的数据 , 严重的情况下容易造成缓存击穿 , 这也是被redis取代的主要原因)
- redis: 持久化 , 可以在宕机恢复后迅速的解决数据丢失的问题
- 问题: 如果在缓存服务器宕机后 , 无法进行数据恢复/没有解决数据丢失的问题 , 会导致“雪崩”(也叫缓存击穿)
1. 雪崩(缓存击穿): 海量用户访问请求涌入 , 一旦缓存失效(宕机后缓存数据丢失) , 所有访问涌入数据库 , 数据库无法承受海量的数据的查询 , 导致数据库服务器宕机 , 这时重启数据库 , 但是请求没有消失甚至用户在不断的刷新(请求瞬间翻倍) , 就发生了数据库在重启->宕机->重启中循环 , 导致整个系统崩溃。- 解决雪崩问题:
- 缓存永不宕机: 启动集群 , 永远让集群的一部分起作用 , 剩余的一部分做备用
- 缓存技术必须要支持恢复数据 , 持久化 。
- 解决雪崩问题:
四、 安装redis
- 登录linux , 并创建管理目录
下载安装包并解压
下载命令wget "http://bj-yzjd.cn-bj.ufileos.com/redis-3.2.11.tar.gz" 解压tar -xvf redis-3.2.11.tar.gz
进入解压之后的文件 , 执行安装
make && make install
启动redis
redis-server
则启动成功
使用redis
- redis启动成功之后该linux主机立即成为一台redis服务器 , 此时使用redis时需要再打开一个该虚拟机的连接之后启动redis客户端
使用redis需要启动redis客户端
redis-cli
如果想在同一个连接中启动服务和客户端 , 则启动redis时可以使redis服务器在后台运行
redis-server &
停止redis服务
- 在占用控制台的服务连接中直接Ctrl+c即可停止服务
在客户端中
shutdown
检查后台 运行的 redis
ps -ef|grep redis
- redis-server 表示redis服务
- *表示所有IP都能够访问当前redis服务 , 如果列出一些列的IP地址 , 则除这些ip地址以外的访问都被拒绝。
五、 redis常用命令
- redis存储的数据实际上是map形式(key-value|{key , value}|list)的字符串
- keys : 获取当前存储空间中所有存在的key
- set [key] [value] : 设置key-value , key和value都是字符串
- get[key] :通过key获取对应的值
- select[整数值0~15] : redis默认存在0~15标号的数据库 分库 , 默认使用第一个库(0号库) , 这个功能是早期版本的冗余功能 , 现在的java代码不支持分库, 所以select 的功能逐渐不被使用
- exists [key] : 判断该key是否存在
- 与get的区别:
- 分析get: 在redis中一个字段允许存储的最大大小为512M
- 如果使用get查询 , 如果存在则会返回值 , 此时如果值过大会占用过多的资源 。
- 而exists只是判断key是否存在 , 如果存在会返回1 , 如果不存在则返回0.
- 与get的区别:
- del [key] : 删除该key对应的键值对
- type [key] : 获取key对应值的类型 , 普通的数据类型都是string , 复杂的数据类型有map 和list
- help type/help[命令名称] 如:help set : 查看该命令的作用
- 实际问题可以在官网中查询对应的命令细节
- flushall : 将所有的数据(0~15号库) , flush到持久化文件中
- flushdb : flush当前分库的所有数据到持久化文件中 。
- incr [key] : 自增 , Integer类型的数据自增(redis中存储是都是String类型 , 在需要自增时, 会先试图转换成Integer在再增) , 如果转换不成功 , 则会报错, incrby [key] index 自增指定的步数
- decr [key]: 自减 decr [key] index 自减指定的步数
- append [key] [appendValue]: 在value后追加数据
- mget [k1] [k2] … : 获取一批key对应的值
- mset [k1] [v1] [k2] [v2] … : 设置一批数据 常用的编程语言的API一般不支持这个命令 ,因为使用这中群体操作k-v的命令后不支持数据分片(使用key取hash值取余后 散列存储)和集群计算 ; 这是一个早期的冗余功能
- expire [key] 时间数字(单位:秒) :设置当前key对应的value的过期时间
- ttl [key] : 查看当前key-value的存活时间
- -2 代表过期
- -1 代表永久
- 可以使用数据中的过期时间来做倒计时 , 或者秒杀 , 但是这个倒计时是秒级别的 。
- ttl [key] : 查看当前key-value的存活时间
- pexpire [key] 时间(单位毫秒) :做精确时间的秒杀
六、 redis其他数据结构
- Hash结构:
- 本身是key-value 的形式 , 但是这里的key也是key-value的形式
- hset [key] [field] [value] : 赋值
- hget [key] [field] : 取值
- hmset [key] [field] [value] [field1] [value1] :批量赋值
- hmget [key] [field] [field1]: 批量取值
- hexists : 查看属性是否存在 , 存在返回1 , 不存在返回0
- hdel [key] [field] :删除字段
- hkeys [key]:只获取字段名
- hvals [key] : 只获取字段值
- hlen [key]:获取字段数量
- list结构
- key-value(双向链表 , 左->上 , 右→下)
- lrange [listkey] start end :查看list
- lpush [key] value :向对应的list的头部添加信息 , 如果没有改;list则创键后添加
- rpush [key] [v1] : 向对应的list的尾部添加字符串元素
- linsert : 向对应的list的特定位置之前或之后添加字符串元素
- lset:设置list中指定下标的元素值
- lrem : 从key对应的list中删除count个value相同的元素
- count>0按从头到尾的顺序删除
- count<0 时按照从尾到头的顺序删除
- count=0 时 删除所有与value相同的元素
- ltrim : 保留指定key的值得范围内的数据
- lpop : 从list头部删除元素, 并返回删除的元素
- rpop : 从list尾部删除元素并返回删除的元素
- rpoplpush [list1] [list2]:从第一个尾部删除一个数据并将删除的数据添加到第二个list头部 , 整个操作是原子级的如果第一个list不存在或为空 则执行结果为nil , 如果第二个list不存在则创建
- lindex : 返回名称为key的list中index位置的元素
- llen : 返回list的长度
七、 数据分布式存储
- 要完成数据的分片存储 , 需要至少多个redis实例
- 启动多个redis时 , 每一个redis会占用一个端口 , 如果端口冲突 , 则会发生启动失败 , 所以要更改redis的默认配置文件
- 修改配置文件
- 进入到redis根目录下的redis.conf文件修改
- 直接输入:set number 使左侧的行号显示
- 第61行 把bind注释掉
- 第80行 保护模式关闭
- 第84行修改默认端口 , 避免和其他redis冲突 , redis默认是6379
- 第105行当客户端空闲时间1小时就自动断开连接 , 0秒表示不启用超时设置
- 第128行daemonize设置成yes让redis启动时由守护进程管理(也就是在后台执行)
- 第150行 , 不同的redis设置不同的pid文件(和端口同名)
- 设置日志级别 , 使用默认就行
- 设置flush动作规则 , 默认900秒以内 至少有1条数据改动 则执行flush , 在300秒以内至少有10条数据变动则执行flush , 在60秒以内至少有10000条数据有变动则执行flush操作 。 默认即可
- 修改完之后保存并退出
- 复制整个redis文件夹 为 r2 , r3
- 并且修改r2 r3中配置文件的端口和pid dump文件的名字
- 进入到r1目录下 , 执行redis-server redis6379.conf
- 进入到r2目录下 , 执行redis-server redis6380.conf
- 进入到r3目录下 , 执行redis-server redis6381.conf
- 执行完之后检测三个redis实例是否启动成功 ps -ef|grep redis
- 此时如果需要开启redis客户端 字需要 执行 redis-cli -p 端口号
八、 Jedis客户端
- redis集群部署完成 , 就是执行数据的存储
- 数据来源: 代码 执行过程中产生
- 如何使用代码来做redis数据的缓存? Jedis客户端
在使用之前需要先导入Jedis的jar包
<jedis.version>2.6.0</jedis.version><!-- jedis --> <dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>${jedis.version}</version> </dependency>
Jedis示例:
/*** 测试单个结点(单个redis)连接* */@Testpublic void test_01() {//创建Jedis对象 , 并在构造方法中设置redis主机的ip和占用的端口Jedis jedis = new Jedis("106.75.48.3",6379);//使用Jedis进行简单的操作//存数据jedis.set("name", "tianjie");//取数据String name = jedis.get("name");System.out.println("name="+name);} /*** 模拟数据缓存执行逻辑 , 数据库的查询操作* */ @Test public void test_02() {System.out.println("用户开始查询数据");//模拟客户端传来的参数String name = "name";//创建一个Jedis客户端Jedis jedis = new Jedis("106.75.48.3" , 6379);//执行逻辑 //1. 先查询缓存中是否有数据 , 如果有则返回缓存中的数据//2. 如果缓存中没有数据则去数据库中查询数据 , 并且 把查询到的数据存入缓存中 , 供后续使用 , 返回数据库中查询到的信息String gname = jedis.get("name");if(gname != null && !gname.equals("")) {//缓存中有数据System.out.println("从缓存中获取到的数据为:name = "+gname);}else {//缓存中没有数据//执行从数据库查询数据 略String dbname = "outman";//数据库查询到的数据//将查询到的数据存入缓存中 jedis.set("name", dbname);//返回从数据库中查到的数据System.out.println("从数据库获取到的数据为:name = "+dbname);}//第一次执行结果为从数据库中获取//第二次执行结果我从缓存中获取数据 }
自定义分片算法 ,将数据分片存入多个redis实例
/*** 自定义分片计算逻辑* */@Testpublic void test_03() {//模拟需要存储的数据String k1 = "四十二章经第一章";String v1 = "111111111111111111";String k2 = "四十二章经第二章";String v2 = "222222222222222222";String k3 = "四十二章经第三章";String v3 = "333333333333333333";List<String> keyList = new ArrayList<String >();keyList.add(k1);keyList.add(k2);keyList.add(k3);Map<String , String > map = new HashMap<String , String >();map.put(k1, v1);map.put(k2, v2);map.put(k3, v3);for(String key : keyList) {if("四十二章经第一章".equals(key)) {//存入第一个redis结点Jedis jedis = new Jedis("106.75.48.3" , 6379);jedis.set(key, map.get(key));}else if("四十二章经第二章".equals(key)) {//存入第二个redis结点Jedis jedis = new Jedis("106.75.48.3" , 6380);jedis.set(key, map.get(key));}else if("四十二章经第三章".equals(key)) {//存入第三个redis结点Jedis jedis = new Jedis("106.75.48.3" , 6381);jedis.set(key, map.get(key));}}}
使用hash取余法将数据分片存储
/*** 哈希取余分片存储逻辑* */@Testpublic void test_04() {//模拟需要被存储的数据List<String> keyList = new ArrayList<String>();Map<String , String> map = new HashMap<String, String>();for(int i = 0 ; i<100 ; i++) {String key = "key_"+i;String value = "value_"+i;keyList.add(key);map.put(key, value);}//使用哈希取余法分片存储//定义结点(redis实例)数量int n = 3;for(String key : keyList) {//执行哈希取余 , 哈希结果可能为负数 , 此时需要与Integer的最大数进行与操作Integer num =( key.hashCode()&Integer.MAX_VALUE )%n; if(num == 0) {//存入第一个结点Jedis jedis = new Jedis("106.75.48.3" , 6379);jedis.set(key, map.get(key));jedis.close();}else if(num == 1) {//存入第二个结点Jedis jedis = new Jedis("106.75.48.3" , 6380);jedis.set(key, map.get(key));jedis.close();}else if(num == 2) {//存入第三个结点Jedis jedis = new Jedis("106.75.48.3" , 6381);jedis.set(key, map.get(key));jedis.close();}}//执行结果 100个键值对几乎均匀的 分布存储在三台redis实例上}
jedis分片 , 使用的hash一致性
/*** Jedis分片 使用哈希一致型完成数据分片存储(Jedis默认的分片算法)* */@Testpublic void test_05() {//需要构造存储多个reids实例信息 的listList<JedisShardInfo> jedisList = new ArrayList<JedisShardInfo>();//创建结点信息JedisShardInfo info1 = new JedisShardInfo("106.75.48.3" , 6379);JedisShardInfo info2 = new JedisShardInfo("106.75.48.3" , 6380);JedisShardInfo info3 = new JedisShardInfo("106.75.48.3" , 6381);//list保存结点信息jedisList.add(info1);jedisList.add(info2);jedisList.add(info3);//构造一个Jedis分片对象 , 将list闯入构造方法中 , 狗后续分片ShardedJedis jedis = new ShardedJedis(jedisList);//模拟海量数据执行数据分片存储for( int i= 0 ; i<1000 ; i++) {jedis.set("key_"+i, "value_" + i);}jedis.close();//数据通过哈希一致型算法分片存储在了多个reids实例中//单数每一个reids的实例的数据量并不是完全平均的 , 会有一定量的数据偏移}
jedis池的使用
/*** Jedis池* */@Testpublic void test_06() {//需要构造存储多个reids实例信息 的listList<JedisShardInfo> jedisList = new ArrayList<JedisShardInfo>();//创建结点信息JedisShardInfo info1 = new JedisShardInfo("106.75.48.3" , 6379);JedisShardInfo info2 = new JedisShardInfo("106.75.48.3" , 6380);JedisShardInfo info3 = new JedisShardInfo("106.75.48.3" , 6381);//list保存结点信息jedisList.add(info1);jedisList.add(info2);jedisList.add(info3);//对于连接来将 类似于JDBC连接处可以设置很多参数JedisPoolConfig config = new JedisPoolConfig();//设置最大连接数config.setMaxTotal(200);//创建Jeids连接池ShardedJedisPool pool = new ShardedJedisPool(config, jedisList);//使用连接处获取数据ShardedJedis jedis = pool.getResource();for(int i = 0 ; i<100 ; i++) {String value = jedis.get("key_"+i);System.out.println("获取到key_"+i+"的值为"+value);}//归还连接pool.returnResource(jedis);}
九、哈希一致性
- 哈希是一种散列算法
- 使用哈希取余算法进行分片存储的问题 :
- 会造成大规模的数据倾斜(散列必定倾斜) , 而哈希一致型一定程度的解决了数据倾斜
- 使用哈希取余算法进行分片 存储之后 , 如果redis实例有变动 , 则数据迁移量过大 。
- 哈希取余算法导致数据迁移量巨大
- 当redis集群数量进行增加减少的时候 , n变化导致数据命中的变化量非常大 , 所以需要进行数据迁移
- 而且redis结点越多 , 数据迁移量越大
哈希一致型
- jedis中引入另外一种hash散列算法 — hash一致性
- 是由1997年麻绳理工的学生发明 : 其原理是引入一个 2^32-1个结点的整数环
- 把节点使用ip+端口做哈希散列计算 , 得到43亿中的一个值 , 投射到环中
- 然后把所有的数据key进行hash散列计算 也投射到环上
- 其中node代表的是redis , 其余的是数据
- 环上的数据会顺时针寻找最近的结点后存储
- 这样在redis增加或减少时 , 数据量的迁移是较少的 , 而且reids结点越多 , 数据量迁移越少
解决数据偏移问题
- 单独的使用节点的ip+端口做映射,毕竟节点数量是有限的
有可能在映射时的各自分布位置并不平均,导致数据偏移量非常大
解决数据的平衡性引入虚拟节点 node1的ip是192.168.40.156 node2的ip是192.168.40.157 各自引入2个虚拟节点(虚拟节点的数量是非常大的) node1-1=hash(192.168.40.156#1) node1-2=hash(192.168.40.156#2) node2-1=hash(192.168.40.157#1) node2-2=hash(192.168.40.157#2) 每一个虚拟节点在哈希环上也会接收顺时针寻找最近节点的key们 通过增加节点数量(虚拟的),完成数据的映射平衡 凡是投影到node1-1,node1-2的key,都会中真实存储在node1中 所以虚拟节点越多平衡性越好
补充:
- 数据库缓存
- hash特性
- 自己查去吧 哈哈
大数据 互联网架构阶段 Redis相关推荐
- 大数据 互联网架构阶段 Redis(三)redis集群
Redis(三) redis集群 一. redis哨兵模式的缺点 问题一 : 横向扩展不方便 , 一旦扩展 , 无论代码结构多么简单, 都需要修改 问题二 : 散列分布式算法是hash一致性 , 无论 ...
- 大数据互联网架构阶段 Redis(二)
Redis(二) 零 . 目录 将缓存引入电商项目 主从复制 哨兵模式 集群容忍度 CAP理论 十. 将缓存引入电商项目 使用Spring框架维护Jedis池对象 引入一个配置文件 applicati ...
- 大数据互联网架构阶段 QuartZ定时任务+RabbitMQ消息队列
QuartZ定时任务+RabbitMQ消息队列 一 .QuartZ定时任务解决订单系统遗留问题 情景分析: 在电商项目中 , 订单生成后 , 数据库商品数量-1 , 但是用户迟迟不进行支付操作 , 这 ...
- 大数据 互联网架构阶段 电商项目简介
电商项目简介 零.目录 电商项目特点 电商项目技术难点 电商项目简介 开发工具 电商项目架构 开发环境 一.电商项目特点 分布式 数十台服务器,甚至百台.千台.万台,包括:Nigix负载均衡集群.To ...
- 大数据互联网架构阶段 大型电商项目数据库设计时应该注意的点
大型电商项目数据库设计时应该注意的点 一. id的设计 int(int)类型: 性能高 , 但是分布式数据库时 , id易重复 long(bigint)类型:性能高 , 比int类型容纳的数据更多 , ...
- 大数据互联网架构阶段 全文检索技术
全文检索 一. 全文检索的引出 我们生活中的数据总体分为两种:结构化数据 和非结构化数据 . 结构化数据: 指具有固定格式或有限长度的数据,如数据库,元数据等. 非结构化数据: 指不定长或无固定格式的 ...
- 大数据互联网架构阶段 前台系统架构 跨域请求
电商项目 前台系统的架构 零.目录 前台架构设计 前台分类树 跨域请求技术 jsonp httpClient 一 .前台架构设计 不能直接访问数据库 , 需要通过后台访问数据 架构: 单通道连接资源 ...
- 大数据 互联网架构阶段 Nginx的使用
Nginx 一.情景分析 如何构建一个tomcat集群 , 两种构建方式 但是此时将项目部署到tomcat集群上之后用户还是只能通过固定的ip和端口访问固定的服务器 ,并没有达到构建tomcat集群时 ...
- 大数据互联网架构阶段 数据库三范式与反范式
数据库范式 一. 三范式 主键: 创建表时可以不设置主键 , 但是没有设置主键的表 , 底层会认为所有的键都是主键 ,所以在创建时使用了所有的字段创建索引 , 在查询时索引的存在几乎没有意义 . 复合 ...
最新文章
- RegularExpressions(4) RegularExpressions 成员(一)
- SDOI2018IIIDX
- 4.边缘光照的描边shader
- 计算机单词修改是否正确,计算机组装必懂的53个单词及装机步骤51条.doc
- 配置V530交换机步骤
- html5手机常亮,vue开发的webapp中的手机物理返回键,以及屏幕常亮处理
- CentOS系统基本设置
- 图:Flash渲染控件安装失败原因所在.
- Spring 测试代码的写法以及一个c3p0的错误
- 漫聊科技发展史——1. 人工智能的发展史
- 论文笔记:Straight to the Tree: Constituency Parsing with Neural Syntactic Distance
- 从配置,外观,写一篇小米13测评报告
- 菜鸟最爱,60行代码打造一款音乐播放器!
- 通过使用阿里云的OCR图文识别 实现识别功能
- 【OpenCV-Python】教程:3-16 利用Grabcut交互式前景提取
- Uniapp Video MP4视频播放失败,只有声音,黑屏,视频播放不了,解决办法
- 电竞英雄联盟数据API接口 - 【比赛列表】API调用示例代码
- 厦门宝讯网捷:拼多多一件代发怎么做?
- NLP语义技术演进:从DP依存句法到SDP依存语义再到AMR抽象语义分析概述与开源实现...
- AMD将坚持x86架构,不会投身ARM架构怀抱
热门文章
- Flask 生成下载文件
- ios NSComparator 三种枚举类型
- Linux下的各文件夹的作用(转)
- [转载]ios简单sqlite使用
- jupyter 数据分析可视化案例_Python数据分析及可视化实例之Anaconda、Jupyter简介
- 天津理工上机c语言报告5,天津理工大学c语言上机报告7.doc
- Java黑皮书课后题第7章:**7.34(对字符串中的字符排序)使用以下方法头编写一个方法,返回一个排序好的字符串。编写一个测试程序,提示用户输入一个字符串,显示排序好的字符串
- C语言学习之怎样引用指针变量
- 华南理工大学和浙大计算机学院,浙江大学和华南理工大学的办学实力比较
- Linux的vim编辑器中的翻页命令