Redis:快速提高系统性能的银弹
来源:互联网 发布:ui和美工哪个工资高 编辑:程序博客网 时间:2024/05/24 02:32
Redis:快速提高系统性能的银弹
作者/分享人:拿客_三产,拿客站长。CSDN博客专家,爱技术,爱分享, 爱开源 Javaer 一枚。CSDN博客专家,曾任职 中通服软件 、智能客、经纶世纪,有在线教育、电子商务、电信运维系统、大数据分析系统开发经验。熟悉 Java Web 、Redis、Kotlin 相关知识,在 sf 论坛举办多次 Redis 分享讲座。
现代高并发复杂系统面临的挑战
动静分离
负载均衡
分布式
集群化
缓存
限流处理
数据压缩
其他
配置管理变得复杂,因此需要设置配置中心来解决该问题。
同一个用户的请求会转发至不同的 Web 服务器,从而导致 Session 丢失等问题。
同一个请求在分布式环境中需要不同服务来提供不同处理,从而需要分布式事务来确保数据的一致性。
分布式唯一 ID 问题。
IP统计
用户登录记录统计
实时的排行榜
原子计数
最新评论
Redis 非银弹却无比接近
Redis 使用 C 开发,是一款内存 K/V 数据库,架构设计极简,性能卓著。
Redis 采用 单线程 多路复用的设计,避免了并发带来的锁性能损耗等问题。
Redis 安装、测试、配置、运维较其他产品更为容易。
Redis 是目前为止最受欢迎的 K/V 数据库,支持持久化,value 支持多种数据结构。
Redis 命令语法简单,极易掌握。
Redis 提供了一种通用的协议,使得各种编程语言都能很方便的开发出与其交互的客户端。
Redis 开放源码,我们可以对其进行二次开发来定制优化。
Redis 目前有较好的社区维护,版本迭代有所保障,新的功能也在有条不紊的添加完善。
Redis 有较好的主从复制、集群相关支持。
最新版本提供模块化功能,可以方便的扩展功能。
配置中心
Redis 本身就是内存 K/V 数据库,支持 哈希、集合、列表等五种数据结构,从而配置信息的存储、读取速度都能够得到满足,Redis 还提供订阅/发布功能从而可以在配置发生改变时通知不同服务器来进行更新相关配置。
分布式锁
使用 Redis 的 SETNX 命令或者 SET 命令配合 NX 选项的方式以及过期时间等功能可以很方便的实现一个性能优越的分布式锁。
缓存
Redis 支持多种过期淘汰机制,本身性能的优势也使 Redis 在缓存方面得到广泛使用。
Lua 脚本
Lua 是一种轻量小巧的脚本语言,用标准C语言编写并开放源代码。Redis 支持 Lua 脚本的运行,从而可以扩展 Redis 中的命令实现很多复杂功能。
Redis 支持使用 Lua 脚本来实现一些组合命令逻辑处理,从而可以使用 Redis 做为限流、分布式唯一 ID 相关技术的实现。
Redis 支持 BitMaps
位图(bitmap)是一种非常常用的结构,在索引,数据压缩等方面有广泛应用,能同时保证存储空间和速度最优化(而不必空间换时间)。
使用 Redis 的 BitMaps 做为用户登录记录统计,不仅统计速度极快,而且内存占用极低。
Redis 支持 HyperLogLog 算法
Redis HyperLogLog是一种使用随机化的算法,以少量内存提供集合中唯一元素数量的近似值。
HyperLogLog 可以接受多个元素作为输入,并给出输入元素的基数估算值:
HyperLogLog 的优点是,即使输入元素的数量或者体积非常非常大,计算基数所需的空间总是固定的、并且是很小的。
在 Redis 里面,每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基数。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明对比。使用 HyperLogLog 算法,我们可以轻而易举的实现 IP 统计等对数据容许些许误差的统计功能。
基数:集合中不同元素的数量。比如 {‘apple’, ‘banana’, ‘cherry’, ‘banana’, ‘apple’} 的基数就是3。
估算值:算法给出的基数并不是精确的,可能会比实际稍微多一些或者稍微少一些,但会控制在合理的范围之内。
Redis 支持 Geo 功能
我们可以使用基于 Redis 来实现地理位置相关管理,附近的人、两地理位置间距离计算等功能变得极为容易实现。
简单消息队列
Redis 列表 + 发布/订阅功能可以很方便的实现一个简单的消息队列,将消息存入 Redis 列表中,通过 发布/订阅功能通知指定成员,成员获取到通知后可以根据通知内容进行对应处理。
全文检索
Redis 官方团队开发了 RediSearch 模块,可以实现使用 Redis 来做全文检索的功能。
分布式唯一ID
Redis 的设计使其可以避免并发的多种问题,使其命令都是原子执行,这些特性都天生匹配分布式唯一ID生成器的要求。
而且通过与 Lua 脚本的结合使用更是能生成复杂的有某些规律的唯一ID。
部分代码实现
Session 共享
将 commons-pool2-2.4.2.jar、jedis-2.9.0.jar、commons-pool2-2.4.2.jar 三个 jar 包放到 Tomcat 下的 lib 目录下(注意:不是项目的 lib 目录)。
修改 Tomcat conf 下 context.xml:
XML <Context> ...... <Valve className="com.orangefunction.tomcat.redissessions.RedisSessionHandlerValve" /> <Manager className="com.orangefunction.tomcat.redissessions.RedisSessionManager" host="127.0.0.1" port="6379" database="0" maxInactiveInterval="60" password="admin123" /> ...... </Context>
java import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONException; import com.coderknock.jedis.executor.JedisExecutor; import com.coderknock.pojo.User; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSessionContext; import java.util.Enumeration; /** * <p></p> * * @author 三产 * @version 1.0 * @date 2017-08-26 * @QQGroup 213732117 * @website http://www.coderknock.com * @copyright Copyright 2017 拿客 coderknock.com All rights reserved. * @since JDK 1.8 */ public class HttpSessionWrapper implements HttpSession { protected final Logger logger = LogManager.getLogger(HttpSessionWrapper.class); private String sid = ""; private HttpServletRequest request; private HttpServletResponse response; private final long creationTime = System.currentTimeMillis(); private final long lastAccessedTime = System.currentTimeMillis(); //过期时间单位秒 private int expire_time = 60; public HttpSessionWrapper() { } public HttpSessionWrapper(String sid, HttpServletRequest request, HttpServletResponse response) { this.sid = sid; this.request = request; this.response = response; } public Object getAttribute(String name) { logger.info(getClass() + "getAttribute(),name:" + name); try { Object obj = JedisExecutor.execute(jedis -> { String jsonStr = jedis.get(sid + ":" + name); if (jsonStr != null || StringUtils.isNotEmpty(jsonStr)) { jedis.expire(sid + ":" + name, expire_time);// 重置过期时间 } return jsonStr; }); return obj; } catch (JSONException je) { logger.error(je); } catch (Exception e) { logger.error(e.getMessage()); } return null; } public void setAttribute(String name, Object value) { logger.info(getClass() + "setAttribute(),name:" + name); try { JedisExecutor.executeNR(jedis -> { if (value instanceof String) { String value_ = (String) value; jedis.set(sid + ":" + name, value_);//普通字符串对象 } else { jedis.set(sid + ":" + name, JSON.toJSONString(value));//序列化对象 } jedis.expire(sid + ":" + name, expire_time);// 重置过期时间 }); } catch (Exception e) { logger.error(e); } } public void removeAttribute(String name) { logger.info(getClass() + "removeAttribute(),name:" + name); if (StringUtils.isNotEmpty(name)) { try { JedisExecutor.executeNR(jedis -> { jedis.del(sid + ":" + name); }); } catch (Exception e) { logger.error(e); } } } //...... 省略部分代码 }
java import com.coderknock.wrapper.DefinedHttpServletRequestWrapper; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import javax.servlet.*; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.UUID; /** * <p></p> * * @author 三产 * @version 1.0 * @date 2017-08-26 * @QQGroup 213732117 * @website http://www.coderknock.com * @copyright Copyright 2017 拿客 coderknock.com All rights reserved. * @since JDK 1.8 */ public class SessionFilter implements Filter { protected final Logger logger = LogManager.getLogger(getClass()); private static final String host = "host"; private static final String port = "port"; private static final String seconds = "seconds"; public void init(FilterConfig filterConfig) throws ServletException { logger.debug("init filterConfig info"); } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { //从cookie中获取sessionId,如果此次请求没有sessionId,重写为这次请求设置一个sessionId HttpServletRequest httpRequest = (HttpServletRequest) request; HttpServletResponse httpResponse = (HttpServletResponse) response; String sid = null; if (httpRequest.getCookies() != null) { for (Cookie cookie : httpRequest.getCookies()) { if (cookie.getName().equals("JSESSIONID")) { sid = cookie.getValue(); break; } } } if (StringUtils.isEmpty(sid)) { try { Cookie cookie = new Cookie("JSESSIONID", httpRequest.getLocalAddr() + ":" + request.getLocalPort() + ":" + UUID.randomUUID().toString().replaceAll("-", "")); httpResponse.addCookie(cookie); } catch (Exception e) { e.printStackTrace(); } } logger.info("JSESSIONID:" + sid); chain.doFilter(new DefinedHttpServletRequestWrapper(sid, httpRequest, httpResponse), response); } public void destroy() { } }
排行榜
# 1、存储几个排行榜成员数据(这里可以理解为把自己系统已有数据加载到 Redis 中) ZADD testTop 23 member1 25 member2 # 2、增加某个人的分数(这里的分数就是排行的依据可以是浮点类型) ZINCRBY testTop 20 member1 # 此时 testTop 中 member1 的分数就编程了 43 ZINCRBY testTop -10 member2 # 此时 testTop 中 member2 的分数就编程了 15 ZINCRBY testTop 20 member3 # 此时向 testTop 中添加了 member3 成员,分数为 20 # 3、查询排行榜前两名,并且查询出其分数【WITHSCORES 选项用于显示分数,不带该参数则只会查出成员名称】 ZREVRANGE testTop 0 1 WITHSCORES #结果: # 1) "member1" # 2) "43" # 3) "member3" # 4) "20" # 假设此时还有一个 排行榜 ZADD testTop2 100 member2 200 member3 123 member4 # 将 testTop testTop2 合成一个总榜 top ZUNIONSTORE top 2 testTop testTop2 # 查询总榜所有成员排行情况 ZREVRANGE top 0 -1 WITHSCORES 1) "member3" 2) "220" 3) "member4" 4) "123" 5) "member2" 6) "115" 7) "member1" 8) "43"
Geo 相关功能
缓存
结束语
附录
Redis环境搭建
Redis 配置
https://www.coderknock.com/blog/2017/06/14/Redis%20%E9%85%8D%E7%BD%AE.html
Redis 支持的五大数据结构
Redis 基础知识扩展阅读
Redis 发布订阅图解
GitChat 是一种全新的阅读/写作互动体验产品。一场 Chat 包含一篇文章和一场为文章的读者和作者定制的专属线上交流。本文出自 Chat 话题《Redis 快速提高系统性能的银弹》。
- Redis 快速提高系统性能的银弹
- Redis:快速提高系统性能的银弹
- 提高系统开发效率的“银弹”——X-series可视化大规模应用开发工具集
- 提高查询系统的性能
- 基于dpdk的redis,性能大大提高
- 【笔者支招:教你快速提高系统的速度与性能】
- 微博遭鹿晗恋情暴击瘫痪,如何快速提高系统性能?
- 微博遭鹿晗恋情暴击瘫痪,如何快速提高系统性能?
- 自己动手提高ubuntu系统的性能
- 如何提高单片机系统的抗干扰性能
- 自己动手提高ubuntu系统的性能(EXT3)
- 如何提高大数据业务系统的性能
- 转:提高系统性能的相关技术
- 自己动手提高ubuntu系统的性能
- 怎样提高系统性能
- 怎样快速估算热点函数的性能提高?
- 怎样快速估算热点函数的性能提高?
- 快速提高 MySQL 性能的 7 个技巧
- CentOS7 下java开发环境小结
- 爬虫系列1查看爬取规则
- 数据库索引底层_B+树详解
- 禅道项目管理系统截图
- 【操作系统】第七章 死锁
- Redis:快速提高系统性能的银弹
- 软件工程(C编码实践篇)学习总结
- Android Framework学习笔记 -- client与AudioFlinger的数据交换
- QtCreator:during startup program exited with code 0xc0000139 QT设置环境变量
- c语言基础01
- volatile变量在多线程同步时运用的优点: 1、锁一次只允许一个线程访问值,volatile 允许多个线程执行读操作,因此当使用 volatile 保证读代码路径时,要比使用锁执行全部代码路径获得
- 批处理常用特殊符号简介
- C++ 11 特性:<algorithm>中的算法
- 32位与64位系统下不同数据类型的大小