新建BaseServlet,maven lib文件夹引用怎么写?

博客分类:
使用Eclipse构建Maven的SpringMVC项目
首先Eclipse需要安装Maven的插件,地址:http://m2eclipse.sonatype.org/sites/m2e。
用MyEclipse安装Maven插件,建出的Maven项目有些问题。一是,发布tomcat的时候resources总是不会被发布到tomcat下;二是,把WEB-INF下的classes改到target下的classes,但是不知道为什么MyEclipse要么仍然在WEB-INF下生成class。要么真不在WEB-INF生成classes了但是发布tomcat的时候,class文件一个都不会给你发布过去,超级郁闷。但是使用Eclipse构建Maven项目后,使用MyEclipse打开就没问题了。
用maven的好处:我感觉最主要就是自动下载jar包和它所依赖的包,这样可以保证了多人开发时jar版本不同的问题。再就是文件结构清晰,java文件,资源文件,测试文件都分的很清楚。
将介绍两种方法:一,直接建立Maven项目方法;二、建立Dynamic Web project转成Maven项目方法。
一、直接建立Maven项目方法
1、建立Maven项目
接下来使用Eclipse的maven构建一个web项目,以构建SpringMVC项目为例:
1.1 选择建立Maven Project
选择File -& New -& Other,在New窗口中选择 Maven -& Maven Project。点击newxt。
1.2 选择项目路径
Use default Workspace location默认工作空间。
1.3 选择项目类型
在Artifact Id中选择maven-archetype-webapp
1.4 输入Group ID和 Artifact ID,以及Package
Group ID一般写大项目名称。Artifact ID是子项目名称。
例如Spring的web包,Group ID:org.springframework,artifactId:spring-web。
Package是默认给你建一个包,不写也可以。
1.5 刚建立好后的文件结构如下图
如果这里显示的内容多,一般是Filters设置的问题。或perspective为JavaEE模式,改成Java模式就可以了。
2、配置Maven项目
接下来就需要更改好多配置了。
2.1 添加Source文件夹
接下来需要添加src/main/java,src/test/java ,src/test/resources三个文件夹。右键项目根目录点击New -& Source Folder,
建出这三个文件夹。注意不是建普通的Folder,而是Source Folder。
2.2 更改class路径
右键项目,Java Build Path -& Source
下面应该有4个文件夹。src/main/java,src/main/resources,src/test/java ,src/test/resources。
双击每个文件夹的Output folder,选择路径。
src/main/java,src/main/resources,选择target/
src/test/java ,src/test/resources, 选择target/test-
选上Allow output folders for source folders.
在此处还要更改:
更改文件夹显示的顺序:点击Order and Export。
更改JDK版本:在Libraries双击JRE System Library,要1.6版本。
2.3 把项目变成Dynamic Web项目
2.3.1 右键项目,选择Project Facets,点击Convert to faceted from
2.3.2 配置Project Facets
更改Dynamic Web Module的Version为2.5。(3.0为Java7的)。
如果提示错误,可能需要在Java Compiler设置Compiler compliance level 为1.6。或者需要在此窗口的Java的Version改成1.6。
2.3.3 配置 Modify Faceted Project
点击Further configuration available…,弹出Modify Faceted Project窗口
此处是设置web.xml文件的路径,我们输入src/main/webapp。
Generate web.xml deployment descriptor自动生成web.xml文件,可选可不选。
2.4 设置部署程序集(Web Deployment Assembly)
上面步骤设置完成后,点击OK,Properties窗口会关闭,在右键项目打开此窗口。在左侧列表中会出现一个Deployment Assembly,点击进去后,如下图:
此处列表是,部署项目时,文件发布的路径。
1,我们删除test的两项,因为test是测试使用,并不需要部署。
2,设置将Maven的jar包发布到lib下。
Add -& Java Build Path Entries -& Maven Dependencies -& Finish
设置完成效果图
3、向maven项目中添加jar包
maven可以管理项目依赖的jar包,通过groupID、artifactId以及版本号可以唯一确定一个jar包。这样可以防止老式Web项目中WEB-INF/lib下jar包不一致的问题。并且maven还会自动下载添加进的jar包所依赖的jar包。
3.1 在pom.xml中添加所需要的jar包
使用Maven POM editor打开项目中的pom.xml文件,选择Dependencies,在Dependencies栏目点击Add进行,首先弹出一个搜索按钮,例如输入spring-web,就会自动搜索关于spring-web相关的jar包,我们选择3.0.5版本的spring。将spring包全部添加进来。需要添加的其他jar包有:junit、jstl。或者点击pom.xml直接编辑pom.xml文件。这样可以直接copy过来dependencies内容。
3.2设置jar包的scope
当添加进入一个jar包后,有一些属性需要设置,最重要的就是scope,它有以下几种取值:
compile,缺省值,适用于所有阶段,会随着项目一起发布。
provided,类似compile,期望JDK、容器或使用者会提供这个依赖。如servlet.jar。
runtime,只在运行时使用,如JDBC驱动,适用运行和测试阶段。
test,只在测试时使用,用于编译和运行测试代码。不会随项目发布。
system,类似provided,需要显式提供包含依赖的jar,Maven不会在 Repository中查找它。
通常SpringMVC项目所需要配置scope的jar包如下图:
有的时候发现servlet-api还是被打包到lib下面了,此时肯定会报错。就需要把maven插件中的WTP也安装一下。
Eclipse在线安装路径:http://m2eclipse.sonatype.org/sites/m2e-extras。选择for Eclipse WTP。
4、构建SpringMVC框架
4.1 编辑web.xml文件
需要添加log4j,字符过滤,Spring 的dispatcher等。
webx.xml代码如下:
&?xml version="1.0" encoding="UTF-8"?&
&web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5" &
&!-- 区分项目名称,防止默认重名 --&
&context-param&
&param-name&webAppRootKey&/param-name&
&param-value&maven.example.root&/param-value&
&/context-param&
&!-- Spring的log4j监听器 --&
&listener&
&listener-class&org.springframework.web.util.Log4jConfigListener&/listener-class&
&/listener&
&!-- 字符集 过滤器
&filter-name&CharacterEncodingFilter&/filter-name&
&filter-class&org.springframework.web.filter.CharacterEncodingFilter&/filter-class&
&init-param&
&param-name&encoding&/param-name&
&param-value&UTF-8&/param-value&
&/init-param&
&init-param&
&param-name&forceEncoding&/param-name&
&param-value&true&/param-value&
&/init-param&
&filter-mapping&
&filter-name&CharacterEncodingFilter&/filter-name&
&url-pattern&/*&/url-pattern&
&/filter-mapping&
&!-- Spring view分发器 --&
&servlet-name&dispatcher&/servlet-name&
&servlet-class&org.springframework.web.servlet.DispatcherServlet&/servlet-class&
&init-param&
&param-name&contextConfigLocation&/param-name&
&param-value&/WEB-INF/dispatcher-servlet.xml&/param-value&
&/init-param&
&load-on-startup&1&/load-on-startup&
&/servlet&
&servlet-mapping&
&servlet-name&dispatcher&/servlet-name&
&url-pattern&*.do&/url-pattern&
&/servlet-mapping&
&/web-app&
4.2 编写Spring配置文件dispatcher-servlet.xml
如要添加MVC驱动、注解检测、视图解析等。dispatcher-servlet.xml代码如下:
&?xml version="1.0" encoding="UTF-8"?&
&beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"&
&mvc:annotation-driven /&
&context:component-scan base-package="liming.maven.example" /&
&bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"&
&property name="prefix" value="/WEB-INF/views/" /&
&property name="suffix" value=".jsp" /&
4.3 编写一个Controller层测试类
编写一个SpringMVC的Controller层测试类。此类只有一个方法做地址映射,并向页面传递一个数据。代码如下:
package liming.maven.example.
import org.springframework.stereotype.C
import org.springframework.ui.M
import org.springframework.web.bind.annotation.RequestM
@Controller
public class GeneralController {
@RequestMapping(value="index.do")
public void index_jsp(Model model){
model.addAttribute("liming", "黎明你好");
System.out.println("index.jsp");
4.4 编写index.jsp页面
首先在src/main/webapp/WEB-INF下建文件夹views。此处和dispatcher-servlet.xml配置文件中的prefix属性路径要一样。
在views下建index.jsp文件
我们使用jstl获取Controlleradd的数据。
Jsp页面代码如下:
&%@ page language="java" contentType="text/ charset=UTF-8" pageEncoding="UTF-8"%&
&%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%&
&!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"&
&meta http-equiv="Content-Type" content="text/ charset=UTF-8"&
&title&Insert title here&/title&
&c:out value="${liming}"&&/c:out&
5,发布到tomcat
这个就没啥说的了。
Eclipse下Tomcat常用设置:http://limingnihao.iteye.com/admin/blogs/825394
访问地址:http://localhost:8080/liming.maven.example/index.do
访问的结果来个截图:
二、建立Dynamic Web project转成Maven项目方法
7、第二种方法DynamicWeb项目转成Mavan
7.1 新建Dynamic Web Project
依次选择选择File -& New -& Other。在New窗口中选择Web下的Dynamic Web Project。点击next。
7.2 配置项目信息
7.2.1 输入项目信息
首先输入项目名称。
Target runtime 一般选择使用什么容器,tomcat之类的。可以先不选。
Dynamic web module version。就是版本号。选择2.5(3.0是Java7的)。
Configuration 是选择容易的配置。如果Target runtime选择了tomcat。这里对应也是选择tomcat。
7.2.2 配置source和output的folder
此窗口输入需要建立的Source folders。和默认output folder。
这个我们的Source folders等建立完项目后在添加。
output folder可以顺手改成target/classes。
7.2.3 配置web.xml文件的路径
7.3 转成maven项目
Dynamic web项目建立完后,首先将其转成maven项目。
右键此项目,选择maven -& Enable Dependency Management。弹出的窗口,直接finish就可以了。
7.4 其他配置
接下来的配置工作就和前面的基本一样了。
请参考上面。
2.1添加Source文件夹
2.2更改class路径。
2.3.3 配置 Modify Faceted Project
2.4 设置部署程序集(Web Deployment Assembly)
接着就是构建SpringMVC框架,发布到tomcat,测试。
下载次数: 3030
下载次数: 1194
浏览 216488
shenselongge 写道在步骤2.1时,添加src/main/java等source folder时报错: The folder is already a source folder.但是这些文件夹在硬盘上确实不存在啊。楼主能否指点下迷津啊?在属性Java Build Path的source,应该报错了,把你物理上没有的删了,重新创建就好了。& 已经解决
在步骤2.1时,添加src/main/java等source folder时报错: The folder is already a source folder.但是这些文件夹在硬盘上确实不存在啊。楼主能否指点下迷津啊?在属性Java Build Path的source,应该报错了,把你物理上没有的删了,重新创建就好了。
可以使用,只不过提示找不到DispatcherServlet,多谢lz少依赖了一个spring-webmvc的包哈~
2.4 设置部署程序集(Web Deployment Assembly)我的eclipse左侧这一步没有Web Deployment Assembly选项怎么办啊??这个问题,我刚才找到一个答案:右击工程属性,找到Project Facets,选择Dynamic Web Module,2.5 点击apply。这样把这个maven工程转换成了web工程这样就有了,但同时会多出一个WebContent文件夹,不知道是否该删除。
& 上一页 1
limingnihao
浏览: 1648802 次
来自: 北京
请问楼主访问controller中的@RequestMappi ...
好详细,写的真全面
哪里有源代码啊,?能否发一份?还有就是 ClassEntity ...
classentity是啥?哪个包的类啊?这教程并不完整啊!
(window.slotbydup=window.slotbydup || []).push({
id: '4773203',
container: s,
size: '200,200',
display: 'inlay-fix'Servlet实现对SQLServer数据库的增删改查(含工程源码)
本文实现了用MyEclipse,编写Servlet,实现对SQLServer数据库的增删改查,适合新手入门,文末提供工程文件源码下载。
1.新建数据库test以及表users
表users共四
本文实现了用MyEclipse,编写Servlet,实现对SQLServer数据库的增删改查,适合新手入门,文末提供工程文件源码下载。
1.新建数据库test以及表users
表users共四列(id,name,psd,tel)
具体操作步骤见上篇博文。
2.新建工程Web Project工程0623p
3. 编辑WebRoot目录下的index.jsp
创建表单(序号、账号、密码、电话、操作),并读取当前数据库内容。
?此处要导入sql_data.java包用于连接数据库(在src目录下新建edu.hwadee.pro.bean包,将sql_data.java复制到此包下即可), &jsp:useBean id="db" class="edu.hwadee.pro.bean.sql_data" /&//id是给bean取个别名,class表示调用的类。
以及导入java.sql.,不然会报错哦。import="java.sql.*"* ?
将修改、删除、添加用户的链接也一并写好。
&%@ page language="java" import="java.util.*" pageEncoding="UTF-8" import="java.sql.*"%&
id="db" class="edu.hwadee.pro.bean.sql_data" /&
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
&!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"&
href="&%=basePath%&"&
&My JSP 'index.jsp' starting page&
http-equiv="pragma" content="no-cache"&
http-equiv="cache-control" content="no-cache"&
http-equiv="expires" content="0"&
http-equiv="keywords" content="keyword1,keyword2,keyword3"&
http-equiv="description" content="This is my page"&
我的首页 &
String sql = "select * from users";
ResultSet rs = null;
rs = db.executeQuery(sql);
//查询数据库,
//executeQuery()方法会把数据库响应的查询结果存放在ResultSet类对象中供我们使用
while (rs.next()) { //rs 游标下移,rs.next()返回一个布尔值,为true,则数据表中有记录。
//next()可以遍历全部数据。
String aa = rs.getString(1); //取出第一个字段的数据
String bb = rs.getString(2);
String cc = rs.getString(3);
String dd = rs.getString(4);
out.print("&tr&&td&" + aa + "&/td&&td&" + bb + "&/td&&td&" + cc
+ "&/td&&td&" + dd
+ "&/td&&td&&a href='servlet/delServlet?id="+aa+"'&删除&/a&、&a href='update.jsp?xh=" + aa + "&&mz="
+ bb + "&&mm=" + cc + "&&dh=" + dd
+ "'&修改&/a&&/td&&/tr&");
href="add.jsp"&添加用户&
启动tomcat服务,将工程发布检测一下(具体步奏见),效果如图
但此时不用去点击删除修改添加这些,因为实现代码还没写好。
4.在src目录下新建edu.hwadee.pro.control包
用于存放增删改查的Servlet程序文件。
在此包下新建Servlet,取名updateServlet,用于修改数据库。
修改doPost()函数,记得导入import edu.hwadee.pro.bean.sql_data包哦,其部分源码如下:
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String aa = null, bb = null, cc = null, dd = null;
aa = request.getParameter("id");
bb = request.getParameter("name");
cc = request.getParameter("psd");
dd = request.getParameter("tel");
String sql = "update users set name='" + bb + "',psd='" + cc
+ "',tel='" + dd + "'where id='" + aa + "'";
sql_data db = new sql_data();
db.executeUpdate(sql);
response.sendRedirect("../index.jsp");
5.在WebRoot目录下新建JSP文件update.jsp
用于创建表单,填写要修改的数据库内容。
源码如下:
&%@ page language="java" contentType="text/ charset=UTF-8"
pageEncoding="UTF-8"%&
&!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"&
http-equiv="Content-Type" content="text/ charset=UTF-8"&
&Insert title here&
String aa=null,bb=null,cc=null,dd=null;
aa=request.getParameter("xh");
bb=request.getParameter("mz");
cc=request.getParameter("mm");
dd=request.getParameter("dh");
action="servlet/updateServlet" method="post"&&%--action调用位置可查看web.xml--%&
序号 name="id" type="hidden" value="&%=aa%&"&&%=aa%&/&&%--将id隐藏,防止用户修改,第二个aa则是将其显示给用户 --%&
账号 name="name" value="&%=bb%&"&/&
密码 name="psd" value="&%=cc%&"&/&
电话 name="tel" value="&%=dd%&"&/&
name="sub" type="submit" value="修改"&/&
发布一下工程,打开index.jsp页面,点击修改,欧耶,是不是能够修改数据内容了呢!!
6.同理,创建addServlet.java和delServlet.java
在edu.hwadee.pro.control目录下再新建两个Servlet,分别是addServlet.java和delServlet.java,并修改其代码,addServlet.java修改doPost()函数,delServlet.java修改doGet()函数,注意要导入import edu.hwadee.pro.bean.sql_data包哦。 addServlet.java部分源码如下:
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String bb = null, cc = null, dd = null;
bb = request.getParameter("name");
cc = request.getParameter("psd");
dd = request.getParameter("tel");
String sql = "insert into users(name,psd,tel) values('" + bb + "','"
+ cc + "','" + dd + "')";
sql_data db = new sql_data();
db.executeInsert(sql);
response.sendRedirect("../index.jsp");
delServlet.java 部分源码如下:
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String id = request.getParameter("id");
String sql = "delete from users where id='"+id+"'";
sql_data db = new sql_data();
db.executeDelete(sql);
response.sendRedirect("../index.jsp");
7. 在WebRoot目录下新建JSP文件add.jsp
用于创建表单,填写要增加的数据库内容。删除数据库内容不需要填写数据,所以就不用写个del.jsp了。
源码如下:
&%@ page language="java" contentType="text/ charset=UTF-8"
pageEncoding="UTF-8"%&
&!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"&
http-equiv="Content-Type" content="text/ charset=UTF-8"&
&Insert title here&
action="servlet/addServlet" method="post"&
账号 name="name" &&
密码 name="psd" &&
电话 name="tel" &&
name="sub" type="submit" value="添加"&&
8. 发布一下工程
打开index.jsp页面,修改、删除、增加用户!!!是不是所有功能都实现了呢,棒棒哒!
9. 工程目录
10. 如果报错
尝试一下方法:
检查一下是否将sqljdbc4.jar复制到
MyEclipse8.5\Common\binary\com.sun.java.jdk.win32.x86_1.6.0.013\jre\lib\ext下
重新发布工程,重启tomcat服务,并重启MyEclipse。
11. 工程源码下载
本文中用到的软件、工具下载地址可从寻找。
转载请注明出处,谢谢。博客地址 。
如有任何问题,欢迎留言。祝君好运!
Life is all about choices!
将来的你一定会感激现在拼命的自己!
版权声明:本文内容由互联网用户自发贡献,版权归作者所有,本社区不拥有所有权,也不承担相关法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件至: 进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容。
用云栖社区APP,舒服~
【云栖快讯】新年大招!云栖社区为在读大学生/研究生准备了一份学(huan)习(zhuang)攻略,发布博文即有机会赢得iPad mini 4等大奖,学习换装两不误!欢迎报名参与~&&
阿里云机器学习是基于阿里云分布式计算引擎的一款机器学习算法平台。用户通过拖拉拽的方式可视化的操作组件来进行试验,...
基于全网公开发布数据、传播路径和受众群体画像,利用语义分析、情感算法和机器学习,分析公众对品牌形象、热点事件和公...
一款阿里巴巴自主研发的高性能、分布式的关系型数据库,支持完整的ACID特性。它高度兼容MySQL协议与语法,让用...
为您提供简单高效、处理能力可弹性伸缩的计算服务,帮助您快速构建更稳定、安全的应用,提升运维效率,降低 IT 成本...
2017阿里千余份技术干货大盘点
Loading...&p&分享一篇文章,作者是简书作者 &a href=&https://link.zhihu.com/?target=https%3A//www.jianshu.com/u/fa& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&kelgon &/a&,原文发表于:&a href=&https://link.zhihu.com/?target=https%3A//www.jianshu.com/p/2f14bc570563& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Redis基础、高级特性与性能调优&/a&。&/p&&p&本文将从Redis的基本特性入手,通过讲述Redis的数据结构和主要命令对Redis的基本能力进行直观介绍。之后在性能调优等方面进行更深入的介绍和指导。&/p&&h2&&b&概述&/b&&/h2&&p&Redis 是一个开源的,基于内存的结构化数据存储媒介,可以作为数据库、缓存服务或消息服务使用。&/p&&p&Redis 支持多种数据结构,包括字符串、哈希表、链表、集合、有序集合、位图、Hyperloglogs 等。&/p&&p&Redis 具备 LRU 淘汰、事务实现、以及不同级别的硬盘持久化等能力,并且支持副本集和通过 Redis Sentinel 实现的高可用方案,同时还支持通过 Redis Cluster 实现的数据自动分片能力。&/p&&p&Redis 的主要功能都基于单线程模型实现,也就是说 Redis 使用一个线程来服务所有的客户端请求,同时 Redis 采用了非阻塞式 IO,并精细地优化各种命令的算法时间复杂度,这些信息意味着:&/p&&ul&&li&Redis 是线程安全的(因为只有一个线程),其所有操作都是原子的,不会因并发产生数据异常&/li&&li&Redis 的速度非常快(因为使用非阻塞式 IO,且大部分命令的算法时间复杂度都是 O(1))&/li&&li&使用高耗时的 Redis 命令是很危险的,会占用唯一的一个线程的大量处理时间,导致所有的请求都被拖慢。(例如时间复杂度为 O(N) 的 KEYS 命令,严格禁止在生产环境中使用)&/li&&/ul&&h2&&b&Redis 的数据结构和相关常用命令&/b&&/h2&&p&本节中将介绍 Redis 支持的主要数据结构,以及相关的常用 Redis 命令。本节只对 Redis 命令进行扼要的介绍,且只列出了较常用的命令。如果想要了解完整的 Redis 命令集,或了解某个命令的详细使用方法,请参考官方文档:&a href=&https://link.zhihu.com/?target=https%3A//redis.io/commands& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&redis.io/commands&/span&&span class=&invisible&&&/span&&/a&&/p&&h2&&b&常用命令一、Key&/b&&/h2&&p&Redis 采用 Key-Value 型的基本数据结构,任何二进制序列都可以作为 Redis 的 Key 使用(例如普通的字符串或一张 JPEG 图片)&br&&b&关于 Key 的一些注意事项:&/b&&/p&&p&不要使用过长的 Key。例如使用一个 1024 字节的 key 就不是一个好主意,不仅会消耗更多的内存,还会导致查找的效率降低&/p&&p&Key 短到缺失了可读性也是不好的,例如”u1000flw” 比起”user:1000:followers” 来说,节省了寥寥的存储空间,却引发了可读性和可维护性上的麻烦&/p&&p&最好使用统一的规范来设计 Key,比如”object-type:id:attr”,以这一规范设计出的 Key 可能是”user:1000” 或”comment:1234:reply-to”&/p&&p&Redis 允许的最大 Key 长度是 512MB(对 Value 的长度限制也是 512MB)&/p&&h2&&b&常用命令二、String&/b&&/h2&&p&String 是 Redis 的基础数据类型,Redis 没有 Int、Float、Boolean 等数据类型的概念,所有的基本类型在 Redis 中都以 String 体现。&/p&&p&与 String 相关的常用命令:&/p&&ul&&li&SET:为一个 key 设置 value,可以配合 EX/PX 参数指定 key 的有效期,通过 NX/XX 参数针对 key 是否存在的情况进行区别操作,时间复杂度 O(1)&/li&&li&GET:获取某个 key 对应的 value,时间复杂度 O(1)&/li&&li&GETSET:为一个 key 设置 value,并返回该 key 的原 value,时间复杂度 O(1)&/li&&li&MSET:为多个 key 设置 value,时间复杂度 O(N)&/li&&li&MSETNX:同 MSET,如果指定的 key 中有任意一个已存在,则不进行任何操作,时间复杂度 O(N)&/li&&li&MGET:获取多个 key 对应的 value,时间复杂度 O(N)&/li&&/ul&&p&上文提到过,Redis 的基本数据类型只有 String,但 Redis 可以把 String 作为整型或浮点型数字来使用,主要体现在 INCR、DECR 类的命令上:&/p&&ul&&li&INCR:将 key 对应的 value 值自增 1,并返回自增后的值。只对可以转换为整型的 String 数据起作用。时间复杂度 O(1)&/li&&li&INCRBY:将 key 对应的 value 值自增指定的整型数值,并返回自增后的值。只对可以转换为整型的 String 数据起作用。时间复杂度 O(1)&/li&&li&DECR/DECRBY:同 INCR/INCRBY,自增改为自减。&/li&&/ul&&p&INCR/DECR 系列命令要求操作的 value 类型为 String,并可以转换为 64 位带符号的整型数字,否则会返回错误。&/p&&p&也就是说,进行 INCR/DECR 系列命令的 value,必须在 [-2^63 ~ 2^63 - 1] 范围内。&/p&&p&前文提到过,Redis 采用单线程模型,天然是线程安全的,这使得 INCR/DECR 命令可以非常便利的实现高并发场景下的精确控制。&/p&&ul&&li&&b&例 1:库存控制&/b&&/li&&/ul&&p&在高并发场景下实现库存余量的精准校验,确保不出现超卖的情况。&/p&&p&设置库存总量:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&SET inv:remain &100&
&/code&&/pre&&/div&&p&库存扣减 + 余量校验:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&DECR inv:remain
&/code&&/pre&&/div&&p&当 DECR 命令返回值大于等于 0 时,说明库存余量校验通过,如果返回小于 0 的值,则说明库存已耗尽。&/p&&p&假设同时有 300 个并发请求进行库存扣减,Redis 能够确保这 300 个请求分别得到 99 到 - 200 的返回值,每个请求得到的返回值都是唯一的,绝对不会找出现两个请求得到一样的返回值的情况。&/p&&ul&&li&&b&例 2:自增序列生成&/b&&/li&&/ul&&p&实现类似于 RDBMS 的 Sequence 功能,生成一系列唯一的序列号&/p&&p&设置序列起始值:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&SET sequence &10000&
&/code&&/pre&&/div&&p&获取一个序列值:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&INCR sequence
&/code&&/pre&&/div&&p&直接将返回值作为序列使用即可。&/p&&p&获取一批(如 100 个)序列值:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&INCRBY sequence 100
&/code&&/pre&&/div&&p&假设返回值为 N,那么 [N - 99 ~ N] 的数值都是可用的序列值。&/p&&p&当多个客户端同时向 Redis 申请自增序列时,Redis 能够确保每个客户端得到的序列值或序列范围都是全局唯一的,绝对不会出现不同客户端得到了重复的序列值的情况。&/p&&h2&&b&常用命令三、List&/b&&/h2&&p&Redis 的 List 是链表型的数据结构,可以使用 LPUSH/RPUSH/LPOP/RPOP 等命令在 List 的两端执行插入元素和弹出元素的操作。虽然 List 也支持在特定 index 上插入和读取元素的功能,但其时间复杂度较高(O(N)),应小心使用。&/p&&p&与 List 相关的常用命令:&/p&&ul&&li&LPUSH:向指定 List 的左侧(即头部)插入 1 个或多个元素,返回插入后的 List 长度。时间复杂度 O(N),N 为插入元素的数量&/li&&li&RPUSH:同 LPUSH,向指定 List 的右侧(即尾部)插入 1 或多个元素&/li&&li&LPOP:从指定 List 的左侧(即头部)移除一个元素并返回,时间复杂度 O(1)&/li&&li&RPOP:同 LPOP,从指定 List 的右侧(即尾部)移除 1 个元素并返回&/li&&li&LPUSHX/RPUSHX:与 LPUSH/RPUSH 类似,区别在于,LPUSHX/RPUSHX 操作的 key 如果不存在,则不会进行任何操作&/li&&li&LLEN:返回指定 List 的长度,时间复杂度 O(1)&/li&&li&LRANGE:返回指定 List 中指定范围的元素(双端包含,即 LRANGE key 0 10 会返回 11 个元素),时间复杂度 O(N)。应尽可能控制一次获取的元素数量,一次获取过大范围的 List 元素会导致延迟,同时对长度不可预知的 List,避免使用 LRANGE key 0 -1 这样的完整遍历操作。&/li&&/ul&&p&应谨慎使用的 List 相关命令:&/p&&ul&&li&LINDEX:返回指定 List 指定 index 上的元素,如果 index 越界,返回 nil。index 数值是回环的,即 - 1 代表 List 最后一个位置,-2 代表 List 倒数第二个位置。时间复杂度 O(N)&/li&&li&LSET:将指定 List 指定 index 上的元素设置为 value,如果 index 越界则返回错误,时间复杂度 O(N),如果操作的是头 / 尾部的元素,则时间复杂度为 O(1)&/li&&li&LINSERT:向指定 List 中指定元素之前 / 之后插入一个新元素,并返回操作后的 List 长度。如果指定的元素不存在,返回 - 1。如果指定 key 不存在,不会进行任何操作,时间复杂度 O(N)&/li&&/ul&&p&由于 Redis 的 List 是链表结构的,上述的三个命令的算法效率较低,需要对 List 进行遍历,命令的耗时无法预估,在 List 长度大的情况下耗时会明显增加,应谨慎使用。&/p&&p&换句话说,Redis 的 List 实际是设计来用于实现队列,而不是用于实现类似 ArrayList 这样的列表的。如果你不是想要实现一个双端出入的队列,那么请尽量不要使用 Redis 的 List 数据结构。&/p&&p&为了更好支持队列的特性,Redis 还提供了一系列阻塞式的操作命令,如 BLPOP/BRPOP 等,能够实现类似于 BlockingQueue 的能力,即在 List 为空时,阻塞该连接,直到 List 中有对象可以出队时再返回。针对阻塞类的命令,此处不做详细探讨,请参考官方文档(&a href=&https://link.zhihu.com/?target=https%3A//redis.io/topics/data-types-intro& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&redis.io/topics/data-ty&/span&&span class=&invisible&&pes-intro&/span&&span class=&ellipsis&&&/span&&/a&) 中”Blocking operations on lists” 一节。&/p&&h2&&b&常用命令四、Hash&/b&&/h2&&p&Hash 即哈希表,Redis 的 Hash 和传统的哈希表一样,是一种 field-value 型的数据结构,可以理解成将 HashMap 搬入 Redis。&/p&&p&Hash 非常适合用于表现对象类型的数据,用 Hash 中的 field 对应对象的 field 即可。&/p&&p&Hash 的优点包括:&/p&&ul&&li&可以实现二元查找,如” 查找 ID 为 1000 的用户的年龄”&/li&&li&比起将整个对象序列化后作为 String 存储的方法,Hash 能够有效地减少网络传输的消耗&/li&&li&当使用 Hash 维护一个集合时,提供了比 List 效率高得多的随机访问命令&/li&&/ul&&p&与 Hash 相关的常用命令:&/p&&ul&&li&HSET:将 key 对应的 Hash 中的 field 设置为 value。如果该 Hash 不存在,会自动创建一个。时间复杂度 O(1)&/li&&li&HGET:返回指定 Hash 中 field 字段的值,时间复杂度 O(1)&/li&&li&HMSET/HMGET:同 HSET 和 HGET,可以批量操作同一个 key 下的多个 field,时间复杂度:O(N),N 为一次操作的 field 数量&/li&&li&HSETNX:同 HSET,但如 field 已经存在,HSETNX 不会进行任何操作,时间复杂度 O(1)&/li&&li&HEXISTS:判断指定 Hash 中 field 是否存在,存在返回 1,不存在返回 0,时间复杂度 O(1)&/li&&li&HDEL:删除指定 Hash 中的 field(1 个或多个),时间复杂度:O(N),N 为操作的 field 数量&/li&&li&HINCRBY:同 INCRBY 命令,对指定 Hash 中的一个 field 进行 INCRBY,时间复杂度 O(1)&/li&&/ul&&p&应谨慎使用的 Hash 相关命令:&/p&&ul&&li&HGETALL:返回指定 Hash 中所有的 field-value 对。返回结果为数组,数组中 field 和 value 交替出现。时间复杂度 O(N)&/li&&li&HKEYS/HVALS:返回指定 Hash 中所有的 field/value,时间复杂度 O(N)&/li&&/ul&&p&上述三个命令都会对 Hash 进行完整遍历,Hash 中的 field 数量与命令的耗时线性相关,对于尺寸不可预知的 Hash,应严格避免使用上面三个命令,而改为使用 HSCAN 命令进行游标式的遍历,具体请见 &a href=&https://link.zhihu.com/?target=https%3A//redis.io/commands/scan& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&redis.io/commands/scan&/span&&span class=&invisible&&&/span&&/a&&/p&&h2&&b&常用命令五、Set&/b&&/h2&&p&Redis Set 是无序的,不可重复的 String 集合。&/p&&p&与 Set 相关的常用命令:&/p&&ul&&li&SADD:向指定 Set 中添加 1 个或多个 member,如果指定 Set 不存在,会自动创建一个。时间复杂度 O(N),N 为添加的 member 个数&/li&&li&SREM:从指定 Set 中移除 1 个或多个 member,时间复杂度 O(N),N 为移除的 member 个数&/li&&li&SRANDMEMBER:从指定 Set 中随机返回 1 个或多个 member,时间复杂度 O(N),N 为返回的 member 个数&/li&&li&SPOP:从指定 Set 中随机移除并返回 count 个 member,时间复杂度 O(N),N 为移除的 member 个数&/li&&li&SCARD:返回指定 Set 中的 member 个数,时间复杂度 O(1)&/li&&li&SISMEMBER:判断指定的 value 是否存在于指定 Set 中,时间复杂度 O(1)&/li&&li&SMOVE:将指定 member 从一个 Set 移至另一个 Set&/li&&/ul&&p&慎用的 Set 相关命令:&/p&&ul&&li&SMEMBERS:返回指定 Hash 中所有的 member,时间复杂度 O(N)&/li&&li&SUNION/SUNIONSTORE:计算多个 Set 的并集并返回 / 存储至另一个 Set 中,时间复杂度 O(N),N 为参与计算的所有集合的总 member 数&/li&&li&SINTER/SINTERSTORE:计算多个 Set 的交集并返回 / 存储至另一个 Set 中,时间复杂度 O(N),N 为参与计算的所有集合的总 member 数&/li&&li&SDIFF/SDIFFSTORE:计算 1 个 Set 与 1 或多个 Set 的差集并返回 / 存储至另一个 Set 中,时间复杂度 O(N),N 为参与计算的所有集合的总 member 数。&/li&&/ul&&p&上述几个命令涉及的计算量大,应谨慎使用,特别是在参与计算的 Set 尺寸不可知的情况下,应严格避免使用。可以考虑通过 SSCAN 命令遍历获取相关 Set 的全部 member(具体请见 &a href=&https://link.zhihu.com/?target=https%3A//redis.io/commands/scan& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&redis.io/commands/scan&/span&&span class=&invisible&&&/span&&/a& ),如果需要做并集 / 交集 / 差集计算,可以在客户端进行,或在不服务实时查询请求的 Slave 上进行。&/p&&h2&&b&常用命令六、Sorted Set&/b&&/h2&&p&Redis Sorted Set 是有序的、不可重复的 String 集合。Sorted Set 中的每个元素都需要指派一个分数 (score),Sorted Set 会根据 score 对元素进行升序排序。如果多个 member 拥有相同的 score,则以字典序进行升序排序。&/p&&p&Sorted Set 非常适合用于实现排名。&/p&&p&Sorted Set 的主要命令:&/p&&ul&&li&ZADD:向指定 Sorted Set 中添加 1 个或多个 member,时间复杂度 O(Mlog(N)),M 为添加的 member 数量,N 为 Sorted Set 中的 member 数量&/li&&li&ZREM:从指定 Sorted Set 中删除 1 个或多个 member,时间复杂度 O(Mlog(N)),M 为删除的 member 数量,N 为 Sorted Set 中的 member 数量&/li&&li&ZCOUNT:返回指定 Sorted Set 中指定 score 范围内的 member 数量,时间复杂度:O(log(N))&/li&&li&ZCARD:返回指定 Sorted Set 中的 member 数量,时间复杂度 O(1)&/li&&li&ZSCORE:返回指定 Sorted Set 中指定 member 的 score,时间复杂度 O(1)&/li&&li&ZRANK/ZREVRANK:返回指定 member 在 Sorted Set 中的排名,ZRANK 返回按升序排序的排名,ZREVRANK 则返回按降序排序的排名。时间复杂度 O(log(N))&/li&&li&ZINCRBY:同 INCRBY,对指定 Sorted Set 中的指定 member 的 score 进行自增,时间复杂度 O(log(N))&/li&&/ul&&p&慎用的 Sorted Set 相关命令:&/p&&ul&&li&ZRANGE/ZREVRANGE:返回指定 Sorted Set 中指定排名范围内的所有 member,ZRANGE 为按 score 升序排序,ZREVRANGE 为按 score 降序排序,时间复杂度 O(log(N)+M),M 为本次返回的 member 数&/li&&li&ZRANGEBYSCORE/ZREVRANGEBYSCORE:返回指定 Sorted Set 中指定 score 范围内的所有 member,返回结果以升序 / 降序排序,min 和 max 可以指定为 - inf 和 + inf,代表返回所有的 member。时间复杂度 O(log(N)+M)&/li&&li&ZREMRANGEBYRANK/ZREMRANGEBYSCORE:移除 Sorted Set 中指定排名范围 / 指定 score 范围内的所有 member。时间复杂度 O(log(N)+M)&/li&&/ul&&p&上述几个命令,应尽量避免传递 [0 -1] 或 [-inf +inf] 这样的参数,来对 Sorted Set 做一次性的完整遍历,特别是在 Sorted Set 的尺寸不可预知的情况下。可以通过 ZSCAN 命令来进行游标式的遍历(具体请见 &a href=&https://link.zhihu.com/?target=https%3A//redis.io/commands/scan& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&redis.io/commands/scan&/span&&span class=&invisible&&&/span&&/a& ),或通过 LIMIT 参数来限制返回 member 的数量(适用于 ZRANGEBYSCORE 和 ZREVRANGEBYSCORE 命令),以实现游标式的遍历。&/p&&h2&&b&常用命令七、Bitmap 和 HyperLogLog&/b&&/h2&&p&Redis 的这两种数据结构相较之前的并不常用,在本文中只做简要介绍,如想要详细了解这两种数据结构与其相关的命令,请参考官方文档 &a href=&https://link.zhihu.com/?target=https%3A//redis.io/topics/data-types-intro& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&redis.io/topics/data-ty&/span&&span class=&invisible&&pes-intro&/span&&span class=&ellipsis&&&/span&&/a& 中的相关章节&/p&&p&Bitmap 在 Redis 中不是一种实际的数据类型,而是一种将 String 作为 Bitmap 使用的方法。可以理解为将 String 转换为 bit 数组。使用 Bitmap 来存储 true/false 类型的简单数据极为节省空间。&/p&&p&HyperLogLogs 是一种主要用于数量统计的数据结构,它和 Set 类似,维护一个不可重复的 String 集合,但是 HyperLogLogs 并不维护具体的 member 内容,只维护 member 的个数。也就是说,HyperLogLogs 只能用于计算一个集合中不重复的元素数量,所以它比 Set 要节省很多内存空间。&/p&&h2&&b&其他常用命令&/b&&/h2&&ul&&li&EXISTS:判断指定的 key 是否存在,返回 1 代表存在,0 代表不存在,时间复杂度 O(1)&/li&&li&DEL:删除指定的 key 及其对应的 value,时间复杂度 O(N),N 为删除的 key 数量&/li&&li&EXPIRE/PEXPIRE:为一个 key 设置有效期,单位为秒或毫秒,时间复杂度 O(1)&/li&&li&TTL/PTTL:返回一个 key 剩余的有效时间,单位为秒或毫秒,时间复杂度 O(1)&/li&&li&RENAME/RENAMENX:将 key 重命名为 newkey。使用 RENAME 时,如果 newkey 已经存在,其值会被覆盖;使用 RENAMENX 时,如果 newkey 已经存在,则不会进行任何操作,时间复杂度 O(1)&/li&&li&TYPE:返回指定 key 的类型,string, list, set, zset, hash。时间复杂度 O(1)&/li&&li&CONFIG GET:获得 Redis 某配置项的当前值,可以使用 * 通配符,时间复杂度 O(1)&/li&&li&CONFIG SET:为 Redis 某个配置项设置新值,时间复杂度 O(1)&/li&&li&CONFIG REWRITE:让 Redis 重新加载 redis.conf 中的配置&/li&&/ul&&h2&&b&Redis 性能调优&/b&&/h2&&p&尽管 Redis 是一个非常快速的内存数据存储媒介,也并不代表 Redis 不会产生性能问题。&br&前文中提到过,Redis 采用单线程模型,所有的命令都是由一个线程串行执行的,所以当某个命令执行耗时较长时,会拖慢其后的所有命令,这使得 Redis 对每个任务的执行效率更加敏感。&/p&&p&针对 Redis 的性能优化,主要从下面几个层面入手:&/p&&ul&&li&最初的也是最重要的,确保没有让 Redis 执行耗时长的命令&/li&&li&使用 pipelining 将连续执行的命令组合执行&/li&&li&操作系统的 Transparent huge pages 功能必须关闭:&/li&&/ul&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&echo never & /sys/kernel/mm/transparent_hugepage/enabled
&/code&&/pre&&/div&&ul&&li&如果在虚拟机中运行 Redis,可能天然就有虚拟机环境带来的固有延迟。可以通过./redis-cli —intrinsic-latency 100 命令查看固有延迟。同时如果对 Redis 的性能有较高要求的话,应尽可能在物理机上直接部署 Redis。&/li&&li&检查数据持久化策略&/li&&li&考虑引入读写分离机制&/li&&/ul&&h2&&b&长耗时命令&/b&&/h2&&p&Redis 绝大多数读写命令的时间复杂度都在 O(1) 到 O(N) 之间,在文本和官方文档中均对每个命令的时间复杂度有说明。&/p&&p&通常来说,O(1) 的命令是安全的,O(N) 命令在使用时需要注意,如果 N 的数量级不可预知,则应避免使用。例如对一个 field 数未知的 Hash 数据执行 HGETALL/HKEYS/HVALS 命令,通常来说这些命令执行的很快,但如果这个 Hash 中的 field 数量极多,耗时就会成倍增长。&/p&&p&又如使用 SUNION 对两个 Set 执行 Union 操作,或使用 SORT 对 List/Set 执行排序操作等时,都应该严加注意。&/p&&p&避免在使用这些 O(N) 命令时发生问题主要有几个办法:&/p&&ul&&li&不要把 List 当做列表使用,仅当做队列来使用&/li&&li&通过机制严格控制 Hash、Set、Sorted Set 的大小&/li&&li&可能的话,将排序、并集、交集等操作放在客户端执行&/li&&li&绝对禁止使用 KEYS 命令&/li&&li&避免一次性遍历集合类型的所有成员,而应使用 SCAN 类的命令进行分批的,游标式的遍历&/li&&/ul&&p&Redis 提供了 SCAN 命令,可以对 Redis 中存储的所有 key 进行游标式的遍历,避免使用 KEYS 命令带来的性能问题。同时还有 SSCAN/HSCAN/ZSCAN 等命令,分别用于对 Set/Hash/Sorted Set 中的元素进行游标式遍历。SCAN 类命令的使用请参考官方文档:&a href=&https://link.zhihu.com/?target=https%3A//redis.io/commands/scan& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&redis.io/commands/scan&/span&&span class=&invisible&&&/span&&/a&&/p&&p&Redis 提供了 Slow Log 功能,可以自动记录耗时较长的命令。相关的配置参数有两个:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&slowlog-log-slower-than xxxms
#执行时间慢于xxx毫秒的命令计入
Slow Logslowlog-max-len xxx
#Slow Log的长度,即最大纪录多少条Slow Log
&/code&&/pre&&/div&&p&使用&b& SLOWLOG GET [number]&/b& 命令,可以输出最近进入 Slow Log 的 number 条命令。&br&使用&b& SLOWLOG RESET&/b& 命令,可以重置 Slow Log&/p&&p&&b&网络引发的延迟&/b&&/p&&ul&&li&尽可能使用长连接或连接池,避免频繁创建销毁连接&/li&&li&客户端进行的批量数据操作,应使用 Pipeline 特性在一次交互中完成。具体请参照本文的 Pipelining 章节&/li&&/ul&&p&&b&数据持久化引发的延迟&/b&&/p&&p&Redis 的数据持久化工作本身就会带来延迟,需要根据数据的安全级别和性能要求制定合理的持久化策略:&/p&&ul&&li&AOF + fsync always 的设置虽然能够绝对确保数据安全,但每个操作都会触发一次 fsync,会对 Redis 的性能有比较明显的影响&/li&&li&AOF + fsync every second 是比较好的折中方案,每秒 fsync 一次&/li&&li&AOF + fsync never 会提供 AOF 持久化方案下的最优性能&br&使用 RDB 持久化通常会提供比使用 AOF 更高的性能,但需要注意 RDB 的策略配置&/li&&li&每一次 RDB 快照和 AOF Rewrite 都需要 Redis 主进程进行 fork 操作。fork 操作本身可能会产生较高的耗时,与 CPU 和 Redis 占用的内存大小有关。根据具体的情况合理配置 RDB 快照和 AOF Rewrite 时机,避免过于频繁的 fork 带来的延迟&/li&&/ul&&blockquote&Redis 在 fork 子进程时需要将内存分页表拷贝至子进程,以占用了 24GB 内存的 Redis 实例为例,共需要拷贝 24GB / 4kB * 8 = 48MB 的数据。在使用单 Xeon 2.27Ghz 的物理机上,这一 fork 操作耗时 216ms。&br&可以通过 INFO 命令返回的 latest_fork_usec 字段查看上一次 fork 操作的耗时(微秒)&/blockquote&&p&&b&Swap 引发的延迟&/b&&/p&&p&当 Linux 将 Redis 所用的内存分页移至 swap 空间时,将会阻塞 Redis 进程,导致 Redis 出现不正常的延迟。Swap 通常在物理内存不足或一些进程在进行大量 I/O 操作时发生,应尽可能避免上述两种情况的出现。&/p&&p&/proc//smaps 文件中会保存进程的 swap 记录,通过查看这个文件,能够判断 Redis 的延迟是否由 Swap 产生。如果这个文件中记录了较大的 Swap size,则说明延迟很有可能是 Swap 造成的。&/p&&p&&b&数据淘汰引发的延迟&/b&&/p&&p&当同一秒内有大量 key 过期时,也会引发 Redis 的延迟。在使用时应尽量将 key 的失效时间错开。&/p&&p&&b&引入读写分离机制&/b&&/p&&p&Redis 的主从复制能力可以实现一主多从的多节点架构,在这一架构下,主节点接收所有写请求,并将数据同步给多个从节点。&/p&&p&在这一基础上,我们可以让从节点提供对实时性要求不高的读请求服务,以减小主节点的压力。&/p&&p&尤其是针对一些使用了长耗时命令的统计类任务,完全可以指定在一个或多个从节点上执行,避免这些长耗时命令影响其他请求的响应。&/p&&p&关于读写分离的具体说明,请参见后续章节&/p&&h2&&b&主从复制与集群分片&/b&&/h2&&p&&b&主从复制&/b&&/p&&p&Redis 支持一主多从的主从复制架构。一个 Master 实例负责处理所有的写请求,Master 将写操作同步至所有 Slave。&/p&&p&借助 Redis 的主从复制,可以实现读写分离和高可用:&/p&&ul&&li&实时性要求不是特别高的读请求,可以在 Slave 上完成,提升效率。特别是一些周期性执行的统计任务,这些任务可能需要执行一些长耗时的 Redis 命令,可以专门规划出 1 个或几个 Slave 用于服务这些统计任务&/li&&li&借助 Redis Sentinel 可以实现高可用,当 Master crash 后,Redis Sentinel 能够自动将一个 Slave 晋升为 Master,继续提供服务&/li&&/ul&&p&启用主从复制非常简单,只需要配置多个 Redis 实例,在作为 Slave 的 Redis 实例中配置:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&slaveof 192.168.1.1 6379
#指定Master的IP和端口
&/code&&/pre&&/div&&p&当 Slave 启动后,会从 Master 进行一次冷启动数据同步,由 Master 触发 BGSAVE 生成 RDB 文件推送给 Slave 进行导入,导入完成后 Master 再将增量数据通过 Redis Protocol 同步给 Slave。之后主从之间的数据便一直以 Redis Protocol 进行同步&/p&&p&&b&使用 Sentinel 做自动 failover&/b&&/p&&p&Redis 的主从复制功能本身只是做数据同步,并不提供监控和自动 failover 能力,要通过主从复制功能来实现 Redis 的高可用,还需要引入一个组件:Redis Sentinel&/p&&p&Redis Sentinel 是 Redis 官方开发的监控组件,可以监控 Redis 实例的状态,通过 Master 节点自动发现 Slave 节点,并在监测到 Master 节点失效时选举出一个新的 Master,并向所有 Redis 实例推送新的主从配置。&/p&&p&Redis Sentinel 需要至少部署 3 个实例才能形成选举关系。&/p&&p&关键配置:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&sentinel monitor mymaster 127.0.0.1 6379 2
#Master实例的IP、端口,以及选举需要的赞成票数
sentinel down-after-milliseconds mymaster 60000
#多长时间没有响应视为Master失效
sentinel failover-timeout mymaster 180000
#两次failover尝试间的间隔时长
sentinel parallel-syncs mymaster 1
#如果有多个Slave,可以通过此配置指定同时从新Master进行数据同步的Slave数,避免所有Slave同时进行数据同步导致查询服务也不可用
&/code&&/pre&&/div&&p&另外需要注意的是,Redis Sentinel 实现的自动 failover 不是在同一个 IP 和端口上完成的,也就是说自动 failover 产生的新 Master 提供服务的 IP 和端口与之前的 Master 是不一样的,所以要实现 HA,还要求客户端必须支持 Sentinel,能够与 Sentinel 交互获得新 Master 的信息才行。&/p&&h2&&b&集群分片&/b&&/h2&&p&为何要做集群分片:&/p&&ul&&li&Redis 中存储的数据量大,一台主机的物理内存已经无法容纳&/li&&li&Redis 的写请求并发量大,一个 Redis 实例以无法承载&/li&&/ul&&p&当上述两个问题出现时,就必须要对 Redis 进行分片了。&/p&&p&Redis 的分片方案有很多种,例如很多 Redis 的客户端都自行实现了分片功能,也有向 Twemproxy 这样的以代理方式实现的 Redis 分片方案。然而首选的方案还应该是 Redis 官方在 3.0 版本中推出的 Redis Cluster 分片方案。&/p&&p&本文不会对 Redis Cluster 的具体安装和部署细节进行介绍,重点介绍 Redis Cluster 带来的好处与弊端。&/p&&h2&&b&Redis Cluster 的能力&/b&&/h2&&ul&&li&能够自动将数据分散在多个节点上&/li&&li&当访问的 key 不在当前分片上时,能够自动将请求转发至正确的分片&/li&&li&当集群中部分节点失效时仍能提供服务&/li&&/ul&&p&其中第三点是基于主从复制来实现的,Redis Cluster 的每个数据分片都采用了主从复制的结构,原理和前文所述的主从复制完全一致,唯一的区别是省去了 Redis Sentinel 这一额外的组件,由 Redis Cluster 负责进行一个分片内部的节点监控和自动 failover。&/p&&h2&&b&Redis Cluster 分片原理&/b&&/h2&&p&Redis Cluster 中共有 16384 个 hash slot,Redis 会计算每个 key 的 CRC16,将结果与 16384 取模,来决定该 key 存储在哪一个 hash slot 中,同时需要指定 Redis Cluster 中每个数据分片负责的 Slot 数。Slot 的分配在任何时间点都可以进行重新分配。&/p&&p&客户端在对 key 进行读写操作时,可以连接 Cluster 中的任意一个分片,如果操作的 key 不在此分片负责的 Slot 范围内,Redis Cluster 会自动将请求重定向到正确的分片上。&/p&&h2&&b&hash tags&/b&&/h2&&p&在基础的分片原则上,Redis 还支持 hash tags 功能,以 hash tags 要求的格式明明的 key,将会确保进入同一个 Slot 中。例如:{uiv}user:1000 和 {uiv}user:1001 拥有同样的 hash tag {uiv},会保存在同一个 Slot 中。&/p&&p&使用 Redis Cluster 时,pipelining、事务和 LUA Script 功能涉及的 key 必须在同一个数据分片上,否则将会返回错误。如要在 Redis Cluster 中使用上述功能,就必须通过 hash tags 来确保一个 pipeline 或一个事务中操作的所有 key 都位于同一个 Slot 中。&/p&&p&有一些客户端(如 Redisson)实现了集群化的 pipelining 操作,可以自动将一个 pipeline 里的命令按 key 所在的分片进行分组,分别发到不同的分片上执行。但是 Redis 不支持跨分片的事务,事务和 LUA Script 还是必须遵循所有 key 在一个分片上的规则要求。&/p&&p&&b&主从复制 vs 集群分片&/b&&/p&&p&在设计软件架构时,要如何在主从复制和集群分片两种部署方案中取舍呢?&/p&&p&从各个方面看,Redis Cluster 都是优于主从复制的方案&/p&&ul&&li&Redis Cluster 能够解决单节点上数据量过大的问题&/li&&li&Redis Cluster 能够解决单节点访问压力过大的问题&/li&&li&Redis Cluster 包含了主从复制的能力&/li&&/ul&&p&那是不是代表 Redis Cluster 永远是优于主从复制的选择呢?&/p&&p&并不是。&/p&&p&软件架构永远不是越复杂越好,复杂的架构在带来显著好处的同时,一定也会带来相应的弊端。采用 Redis Cluster 的弊端包括:&/p&&ul&&li&维护难度增加。在使用 Redis Cluster 时,需要维护的 Redis 实例数倍增,需要监控的主机数量也相应增加,数据备份 / 持久化的复杂度也会增加。同时在进行分片的增减操作时,还需要进行 reshard 操作,远比主从模式下增加一个 Slave 的复杂度要高。&/li&&li&客户端资源消耗增加。当客户端使用连接池时,需要为每一个数据分片维护一个连接池,客户端同时需要保持的连接数成倍增多,加大了客户端本身和操作系统资源的消耗。&/li&&li&性能优化难度增加。你可能需要在多个分片上查看 Slow Log 和 Swap 日志才能定位性能问题。&/li&&li&事务和 LUA Script 的使用成本增加。在 Redis Cluster 中使用事务和 LUA Script 特性有严格的限制条件,事务和 Script 中操作的 key 必须位于同一个分片上,这就使得在开发时必须对相应场景下涉及的 key 进行额外的规划和规范要求。如果应用的场景中大量涉及事务和 Script 的使用,如何在保证这两个功能的正常运作前提下把数据平均分到多个数据分片中就会成为难点。&/li&&/ul&&p&所以说,在主从复制和集群分片两个方案中做出选择时,应该从应用软件的功能特性、数据和访问量级、未来发展规划等方面综合考虑,只在&b&确实有必要&/b&引入数据分片时再使用 Redis Cluster。&/p&&p&下面是一些建议:&/p&&blockquote&需要在 Redis 中存储的数据有多大?未来 2 年内可能发展为多大?这些数据是否都需要长期保存?是否可以使用 LRU 算法进行非热点数据的淘汰?综合考虑前面几个因素,评估出 Redis 需要使用的物理内存。&br&用于部署 Redis 的主机物理内存有多大?有多少可以分配给 Redis 使用?对比 (1) 中的内存需求评估,是否足够用?&br&Redis 面临的并发写压力会有多大?在不使用 pipelining 时,Redis 的写性能可以超过 10 万次 / 秒(更多的 benchmark 可以参考 &a href=&https://link.zhihu.com/?target=https%3A//redis.io/topics/benchmarks& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&redis.io/topics/benchma&/span&&span class=&invisible&&rks&/span&&span class=&ellipsis&&&/span&&/a& )&br&在使用 Redis 时,是否会使用到 pipelining 和事务功能?使用的场景多不多?&/blockquote&&p&综合上面几点考虑,如果单台主机的可用物理内存完全足以支撑对 Redis 的容量需求,且 Redis 面临的并发写压力距离 Benchmark 值还尚有距离,建议采用主从复制的架构,可以省去很多不必要的麻烦。同时,如果应用中大量使用 pipelining 和事务,也建议尽可能选择主从复制架构,可以减少设计和开发时的复杂度。&/p&
分享一篇文章,作者是简书作者 ,原文发表于:。本文将从Redis的基本特性入手,通过讲述Redis的数据结构和主要命令对Redis的基本能力进行直观介绍。之后在性能调优等方面进行更深入的介绍和指导。概述Redis 是一个开源…
&p&题主提的4个问题,分别指向了数据分析入门中【学习材料选择】【数据资源获取】【数据分析项目实践】的关键问题,这也是我们在设计&a href=&//link.zhihu.com/?target=https%3A//www.kesci.com/apps/home/project/5ab295c1ecff2& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&「DATA TRIAN」&i class=&icon-external&&&/i&&/a&系列活动时,致力回答的问题。&/p&&p&数联寻英于2016年发布的《大数据人才报告》显示,未来3-5年数据人才缺口达150万之多,数据分析师的价值日益凸显,越来越多的人开始了数据分析从0到1的学习。&/p&&p&而现实问题是:数据知识是相对零散的,真实行业核心数据资源是难以获取的,企业内数据化创新应用的学习案例是稀缺的,数据分析的完整学习闭环难以构建(掌握知识&技能-掌握工具-解决真实商业问题-推动商业决策-实现个人发展)。&/p&&p&在&a href=&//link.zhihu.com/?target=https%3A//www.kesci.com/apps/home/project/5ab295c1ecff2& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&「DATA TRIAN」&i class=&icon-external&&&/i&&/a&系列活动中,我们期望为解决上述问题做一些贡献。&/p&&p&&br&&/p&&blockquote&精选实用基础教程,帮你完成学习素材筛选、学习节奏部署&/blockquote&&p&&b&Week1&/b&&/p&&p&学习素材&/p&&ul&&li&&u&&a href=&//link.zhihu.com/?target=https%3A//www.kesci.com/apps/home/project/59e55c499bc3& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&科赛 x 机器之心 从零上手Python关键代码&i class=&icon-external&&&/i&&/a&&/u&&/li&&li&&u&&a href=&//link.zhihu.com/?target=https%3A//www.kesci.com/apps/home/project/59e389bc48f518& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Pandas基础命令速查表&i class=&icon-external&&&/i&&/a&&/u&&/li&&li&&u&&a href=&//link.zhihu.com/?target=https%3A//www.kesci.com/apps/home/project/59e77a636ddaec2& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&这十套练习,教你如何使用Pandas做数据分析&i class=&icon-external&&&/i&&/a&&/u&&/li&&/ul&&p&内容说明&/p&&ul&&li&Python关键代码的学习对数据科学、网页开发、机器学习均为十分重要且常见的语法知识。&/li&&li&第二部分对 Pandas 最基础的命令代码及使用方式进行讲解,并配套相应的十套练习题。请各位同学使用&u&&a href=&//link.zhihu.com/?target=https%3A//www.kesci.com/apps/home/dataset/59e715b76dd4507& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Pandas练习题&i class=&icon-external&&&/i&&/a&&/u&数据集进行训练。&/li&&/ul&&p&&b&Week2&/b&&/p&&p&学习素材&/p&&ul&&li&&u&&a href=&//link.zhihu.com/?target=https%3A//www.kesci.com/apps/home/project/59ed832d18eca968& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Numpy快速上手指南 --- 基础篇&i class=&icon-external&&&/i&&/a&&/u&&/li&&li&&u&&a href=&//link.zhihu.com/?target=https%3A//www.kesci.com/apps/home/project/59fb& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Numpy快速上手指南 --- 进阶篇&i class=&icon-external&&&/i&&/a&&/u&&/li&&li&&u&&a href=&//link.zhihu.com/?target=https%3A//www.kesci.com/apps/home/project/59f29f67c5f3fcc& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&这100道练习,带你玩转Numpy&i class=&icon-external&&&/i&&/a&&/u&&/li&&/ul&&p&内容说明&/p&&ul&&li&Python三大核心工具包之一——Numpy教程&/li&&/ul&&p&&b&Week3&/b&&/p&&p&学习素材&/p&&ul&&li&&u&&a href=&//link.zhihu.com/?target=https%3A//www.kesci.com/apps/home/project/59ed8da9b4c0& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&从零开始学Python【1】--matplotlib(条形图)&i class=&icon-external&&&/i&&/a&&/u&&/li&&li&&u&&a href=&//link.zhihu.com/?target=https%3A//www.kesci.com/apps/home/project/59f6de30c5f3f1& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&从零开始学Python【2】--matplotlib(饼图)&i class=&icon-external&&&/i&&/a&&/u&&/li&&li&&u&&a href=&//link.zhihu.com/?target=https%3A//www.kesci.com/apps/home/project/59f6ec6dc5f3fe& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&从零开始学Python【3】--matplotlib(箱线图)&i class=&icon-external&&&/i&&/a&&/u&&/li&&/ul&&p&内容说明&/p&&ul&&li&Matplotlib的设计理念是能够用轻松简单的方式生成强大的可视化效果,是Python学习过程中核心库之一。 &/li&&/ul&&p&&b&Week4&/b&&/p&&p&学习素材&/p&&ul&&li&&u&&a href=&//link.zhihu.com/?target=https%3A//www.kesci.com/apps/home/project/59c662398bfc& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&seaborn可视化学习之 categorial visualization&i class=&icon-external&&&/i&&/a&&/u&&/li&&li&&u&&a href=&//link.zhihu.com/?target=https%3A//www.kesci.com/apps/home/project/59c662398bfc& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&seaborn可视化之time series & regression & heatmap&i class=&icon-external&&&/i&&/a&&/u&&/li&&li&&u&&a href=&//link.zhihu.com/?target=https%3A//www.kesci.com/apps/home/project/59f6ec6dc5f3fe& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&seaborn可视化学习之distribution visualization)&i class=&icon-external&&&/i&&/a&&/u&&/li&&/ul&&p&内容说明&/p&&p&seaborn是一个很棒的可视化库,尤其是当数据维度很大的时候,seaborn可以让我们用较少的代码去绘制一些描述性统计的图,便于找寻各维度变量之间的特征。&/p&&p&&br&&/p&&blockquote&提供K - Lab在线数据分析协作平台,工具安装不再困扰&/blockquote&&p&亲手搭建本地编程环境的繁琐工程量、过程中冒出的各种安装Bug,分分钟让人丧失学习兴趣。&/p&&p&而这次,我们期望把数据分析学习的启动成本降到最低:K-Lab在线数据分析协作平台,已经为你集成Python3、Python2、R三种主流编程语言环境,同步内置100+常用数据分析工具包,用户完成登录后,可以直接在云端学习编程语言,开展数据分析。&/p&&figure&&img src=&https://pic3.zhimg.com/v2-4b73e6e2e7dcab1f3a75a3ba_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&750& data-rawheight=&481& class=&origin_image zh-lightbox-thumb& width=&750& data-original=&https://pic3.zhimg.com/v2-4b73e6e2e7dcab1f3a75a3ba_r.jpg&&&/figure&&p&用户只需点击「Fork」键,即可完整复现&a href=&//link.zhihu.com/?target=https%3A//www.kesci.com/apps/home/project/5ab295c1ecff2& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&「DATA TRAIN | 数据分析学习计划」&i class=&icon-external&&&/i&&/a&中的基础学习教程,体验「边敲代码边运行边掌握知识」的实践化学习场景。&/p&&p&&br&&/p&&blockquote&数据分析项目实践,学习成果阶段性检验&/blockquote&&p&当你按计划完成学习,可以参与完成我们设置的一个小任务:&a href=&//link.zhihu.com/?target=https%3A//www.kesci.com/apps/home/dataset/58acf28cdb401c& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Movielens/IMDB电影数据分析&i class=&icon-external&&&/i&&/a&,看看自己是否能用Python进行简单的数据分析。&/p&&p&&br&&/p&&figure&&img src=&https://pic1.zhimg.com/v2-8907feb0a4101eacf92fa4_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&856& data-rawheight=&568& class=&origin_image zh-lightbox-thumb& width=&856& data-original=&https://pic1.zhimg.com/v2-8907feb0a4101eacf92fa4_r.jpg&&&/figure&&p&&br&&/p&&blockquote&对接真实商业数据问题,数据分析能力逐步进阶&/blockquote&&p&真正有价值的数据工作在于通过分析影响业务决策、创造商业价值&/p&&p&我们同步在科赛官网开放&a href=&//link.zhihu.com/?target=https%3A//www.kesci.com/apps/home/competition/59c1db9b4477& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&「金融行业数据算法训练营」&i class=&icon-external&&&/i&&/a&,选用往期「&a href=&//link.zhihu.com/?target=https%3A//www.kesci.com/apps/home/competition/58e46b3b9ed26b1e09bfbbb7/works& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&前海征信“好信杯”大数据算法大赛&i class=&icon-external&&&/i&&/a&」为案例,拆解出学习教程,学有所余的童鞋们不如挑战一番,实现数据分析能力逐步进阶。&/p&&p&我们已配备作品在线提交及测评系统,将根据事先设定的「测评标准」进行排名并实时更新。方便你对自我数据分析能力进行合理评估,并持续优化、提升。&/p&&figure&&img src=&https://pic1.zhimg.com/v2-0dc65d0beceb6babea3ba87e92925f74_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1080& data-rawheight=&1800& class=&origin_image zh-lightbox-thumb& width=&1080& data-original=&https://pic1.zhimg.com/v2-0dc65d0beceb6babea3ba87e92925f74_r.jpg&&&/figure&&p&&br&&/p&&p&行远自迩,祝大家早日实现质的蜕变~~&/p&
题主提的4个问题,分别指向了数据分析入门中【学习材料选择】【数据资源获取】【数据分析项目实践】的关键问题,这也是我们在设计系列活动时,致力回答的问题。数联寻英于2016年发布的《大数据人才报告》显示,未来3-5年数据人才缺口达150万…
&figure&&img src=&https://pic2.zhimg.com/v2-dde3f6a9ed7e05e40f84ba_b.jpg& data-rawwidth=&902& data-rawheight=&645& class=&origin_image zh-lightbox-thumb& width=&902& data-original=&https://pic2.zhimg.com/v2-dde3f6a9ed7e05e40f84ba_r.jpg&&&/figure&&p&今天紧接昨天的内容,跟大家分享如何使用REmap函数制作路径图。&/p&&p&路径图所需要的数据结构非常简单,两列数据,左侧是起点,右侧是终点,并且每一行的终点是下一行的起点,这样最终才可以制作出连接在一起的路径图。&/p&&p&首先我们来构造所需的数据:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&起点数据:
origin&- c(&beijing&,&shijiazhuang&,&zhengzhou&,&hefei&,&nanjing&,&济南&,&dalian&)
终点数据:
destination&- c(origin[-1],origin[1]) #将起点数据首尾互换,并构造终点数据
&/code&&/pre&&/div&&p&将终点数据、起点数据合并为数据框格式的作图数据:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&map_data&- data.frame(origin,destination)
&/code&&/pre&&/div&&figure&&img src=&https://pic1.zhimg.com/v2-9a64ee3edfdd_b.jpg& data-rawwidth=&663& data-rawheight=&395& class=&origin_image zh-lightbox-thumb& width=&663& data-original=&https://pic1.zhimg.com/v2-9a64ee3edfdd_r.jpg&&&/figure&&p&绘图:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&map_out1&- remap(mapdata=map_data,
title =&我是主标题&,
subtitle =&我是副标题&,
theme =get_theme(theme='Dark')
plot(map_out1)
#在web上展示图形
&/code&&/pre&&/div&&figure&&img src=&https://pic1.zhimg.com/v2-8c6b9d8fd4eea93a8f72c12e633d2037_b.jpg& data-rawwidth=&687& data-rawheight=&503& class=&origin_image zh-lightbox-thumb& width=&687& data-original=&https://pic1.zhimg.com/v2-8c6b9d8fd4eea93a8f72c12e633d2037_r.jpg&&&/figure&&p&上面的例子中,为了使得路径图首尾相连,终点数据是起点数据调换首尾行而得到的。&/p&&p&那么如果不要求路径图首尾相连的话可以设置如下结构:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&map_data1&-map_data[-7,]
map_out2&- remap(mapdata=map_data1,
title =&我是主标题&,
subtitle =&我是副标题&,
theme =get_theme(theme='Dark')
plot(map_out2)
#在web上展示图形
&/code&&/pre&&/div&&figure&&img src=&https://pic3.zhimg.com/v2-882c2da38696baa9b9be8_b.jpg& data-rawwidth=&670& data-rawheight=&499& class=&origin_image zh-lightbox-thumb& width=&670& data-original=&https://pic3.zhimg.com/v2-882c2da38696baa9b9be8_r.jpg&&&/figure&&p&这种路径图的形式非常适合用于表达带有很多中间节点的动态路线。&/p&&p&当然如果你也可以将数据源设置成两条毫不相干的路线:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&data1&-c(&西安&,&zhengzhou&,&shijiazhuang&,&beijing&,&shenyang&,&changhcun&,&哈尔滨&)
data2&-c(&nanyang&,&wuhan&,&changsha&,&南昌&,&guangzhou&,&南宁&,&贵阳&)
origin&-c(data1[1:6],data2[1:6])
destination&-c(data1[2:7],data2[2:7])
map_data1&- data.frame(origin,destination)
&/code&&/pre&&/div&&figure&&img src=&https://pic7.zhimg.com/v2-8eddf56d5ae9d168e02e194bd95e6aa8_b.jpg& data-rawwidth=&692& data-rawheight=&434& class=&origin_image zh-lightbox-thumb& width=&692& data-original=&https://pic7.zhimg.com/v2-8eddf56d5ae9d168e02e194bd95e6aa8_r.jpg&&&/figure&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&map_out3&- remap(mapdata=map_data1,
title =&我是主标题&,
subtitle =&我是副标题&,
theme =get_theme(theme='Dark')
plot(map_out3)
&/code&&/pre&&/div&&figure&&img src=&https://pic1.zhimg.com/v2-c1fd81e5c5cffd079d79ea27eb51d96e_b.jpg& data-rawwidth=&575& data-rawheight=&441& class=&origin_image zh-lightbox-thumb& width=&575& data-original=&https://pic1.zhimg.com/v2-c1fd81e5c5cffd079d79ea27eb51d96e_r.jpg&&&/figure&&p&随机生成的线条和气泡颜色看着挺别扭,我们可以将其修改为自定义颜色:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&map_out4&- remap(mapdata=map_data1,
title =&我是主标题&,
subtitle =&我是副标题&,
theme =get_theme(theme='none',
lineColor = &white&,
backgroundColor=&black&
plot(map_out4)
&/code&&/pre&&/div&&figure&&img src=&https://pic2.zhimg.com/v2-baaf477accd5fd576b0e_b.jpg& data-rawwidth=&902& data-rawheight=&645& class=&origin_image zh-lightbox-thumb& width=&902& data-original=&https://pic2.zhimg.com/v2-baaf477accd5fd576b0e_r.jpg&&&/figure&&p&你可以用这种方法,结合自己的数据来表达多个地域之间的流动方向:比如一打一路、长征、贸易路线、铁路路线图等都非常适合这种表达。&/p&&p&gif效果:&/p&&figure&&img src=&https://pic4.zhimg.com/v2-c6c04518b4ade676ebfbeefa_b.jpg& data-rawwidth=&847& data-rawheight=&624& data-thumbnail=&https://pic4.zhimg.com/v2-c6c04518b4ade676ebfbeefa_b.jpg& class=&origin_image zh-lightbox-thumb& width=&847& data-original=&https://pic4.zhimg.com/v2-c6c04518b4ade676ebfbeefa_r.gif&&&/figure&&p&如果想要存储该HTML对象,需要设定临时目录:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&setwd(&D:/R/Rscript&)
#保存图片的位置,不做修改默认为R的工作路径
options(remap.js.web=T)
#动态网页图保存命令
plot(map_out5)
#保存的同时自动调用浏览器窗口
&/code&&/pre&&/div&&h2&联系方式:&/h2&&p&wechat:ljty1991 &br&Mail: &br&个人公众号:数据小魔方(datamofang) &br&团队公众号:EasyCharts &br&qq交流群:[魔方学院]&/p&
今天紧接昨天的内容,跟大家分享如何使用REmap函数制作路径图。路径图所需要的数据结构非常简单,两列数据,左侧是起点,右侧是终点,并且每一行的终点是下一行的起点,这样最终才可以制作出连接在一起的路径图。首先我们来构造所需的数据:起点数据:
&figure&&img src=&https://pic2.zhimg.com/v2-7e5dbceb1d86_b.jpg& data-rawwidth=&1387& data-rawheight=&674& class=&origin_image zh-lightbox-thumb& width=&1387& data-original=&https://pic2.zhimg.com/v2-7e5dbceb1d86_r.jpg&&&/figure&&p&今天开始,要跟着大家学习一个新的可视化R包——REmap包。&/p&&p&说起来,这个包要归功于百度的可视化开源项目——echarts,它是国内为数不多的高水平开源可视化js库,在业界地位首屈一指。&/p&&p&经过大神的努力,我们终于可以在R语言中以简洁的REmap函数形式,来调用Echarts核心功能,而不用亲自去学js代码,就可以创建动态地图可视化作品。&/p&&p&如果有小伙伴儿熟悉百度的大数据可视化项目——百度迁徙地图,以及交通通勤图,以及后来的一带一路可视化路线图,肯定对那些动态地图上流动的线条和路径有很深的印象。&/p&&p&下面我们开始简单介绍REmap的主要功能:&/p&&p&REmap包中涵盖有一组函数,我所知道并用过的有REmap、REmapB、REmapC、REmapH。&/p&&p&每一个函数都对应着几种炫酷的动态效果类型:&/p&&p&今天我们先介绍REmap函数并利用该函数制作简单的流向图:&/p&&p&安装及加载:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&install.packages(&devtools&)
devtools::install_github(&lchiffon/REmap&)
#开发者/包名
library(REmap)
&/code&&/pre&&/div&&p&REmap函数的作图理念与ggplot其实还是有雷同的,它也是将数据信息和美学信息分开表达,数据信息包含在数据表中,而美化修饰参数都包含在theme内。&/p&&p&remap(mapdata, title = & &, &/p&&p&subtitle = &&, &/p&&p&theme = get_theme(&Dark&))&/p&&p&这是一个典型的REmap函数的结构,mapdata是我们所要展示的数据框格式数据集,里面包含了两列数据:起点位置、终点信息。&/p&&p&title用于设置主标题、subtitle用于设置副标题。而theme用于设定整个图形的主题风格,该主题是一个封装的函数变量,里面包含诸多参数信息(各种点线面的颜色、度量等信息)。&/p&&p&下面是一个典型的REmap主题内的参数示例:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&get_theme(theme = &Dark&,
lineColor = &Random&,
backgroundColor = &#1b1b1b&,
titleColor = &fff&,
borderColor = &rgba(100,149,237,1)&,
regionColor = &#1b1b1b&,
labelShow = T,
pointShow = F,
pointColor = &gold&)
&/code&&/pre&&/div&&p&参数说明:&br&&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&参数一:theme为总体配色,默认选项有“Dark”、“Bright”、“Sky”、“none”
参数二:lineColor为线条颜色,默认是随机, 也可自行设置,如’red’
参数三:backgroundColor为地图外背景颜色
参数四:titleColor为标题颜色
参数五:borderColor为地图中地域边缘颜色,不同省份、城市的边缘
参数六:regionColor为地域颜色,即地图显示的颜色
参数七:labelShow为逻辑参数,是否显示地域名称,默认为T,即显示,只用于remapC函数
参数八:pointShow为逻辑参数,是否显示地域所在点,默认为F,不显示,只用于remapC函数
参数九:pointColor为pointShow中点的颜色,只用于remapC函数
&/code&&/pre&&/div&&p&REmap可以非常轻松的获取城市地点的经纬度数据:&/p&&p&以大连为例:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&get_city_coord(&大连&)
[1] 121.634
&/code&&/pre&&/div&&p&如果想要获取一组城市的经纬度信息,可以直接赋值给城市向量:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&city_list &- c(&beijing&,&tianjin&,&shenyang&,&dalian&,&zhengzhou&)
get_geo_position(city_list)
&/code&&/pre&&/div&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&
5 113.49 zhengzhou
&/code&&/pre&&/div&&p&而且据说这两个函数大部分时候对拼音与汉字,甚至拼音的大小写都不敏感,所以避免了很多麻烦。&/p&&p&创建起始点:&/p&&figure&&img src=&https://pic1.zhimg.com/v2-ea41bd2ec33d537e7576cbbb4d620837_b.jpg& data-rawwidth=&766& data-rawheight=&94& class=&origin_image zh-lightbox-thumb& width=&766& data-original=&https://pic1.zhimg.com/v2-ea41bd2ec33d537e7576cbbb4d620837_r.jpg&&&/figure&&p&#合成数据框格式的起终点数据&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&map_data&- data.frame(origin,destination)
&/code&&/pre&&/div&&p& 数据预览:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&head(map_data)
origin destination
&/code&&/pre&&/div&&p&作图函数:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&map_out&-remap(mapdata=map_data,
#流向地图的数据源(依次为起点、重点两列)
title=&我是标题&,
#设置主标题
subtitle=&我是副标题&,
#设置副标题
theme =get_theme(theme=&Bright&))
#设置主题(默认主题一共有三套:“Dark”,“Bright,”Sky“)
&/code&&/pre&&/div&&p&运行以上代码之后,动态图表已经生成名为map_out的对象,只需使用plot函数调用一下map_out图表对象就可以看到该动态图表效果。&/p&&p&::提示下,这里的map_out的对象是一个js代码生成的html对象,运行完plot之后,动态图表效果不是呈现在R的图表窗口而是调用了桌面浏览器的Web窗口。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&plot(map_out)
Save img as: C:\Users\ADMINI~1\AppData\Local\Temp\RtmpAxQndv/ID_55_242761.html
&/code&&/pre&&/div&&p&运行完成之后,浏览器窗口会自动呈现动态图表效果:&br&&/p&&figure&&img src=&https://pic3.zhimg.com/v2-c48fbdf3f963c3f0e74868_b.jpg& data-rawwidth=&1387& data-rawheight=&674& class=&origin_image zh-lightbox-thumb& width=&1387& data-original=&https://pic3.zhimg.com/v2-c48fbdf3f963c3f0e74868_r.jpg&&&/figure&&p&其实仔细看以上代码,与ggplot的地图代码相比,要简单很多,数据集也很简单(最重要的直接支持地点名称而不需要经纬度数据),当然这些便利性主要归功于它的底层代码是调用Echarts项目,地理位置信息有百度地图的强大系统支持。&br&&/p&&p&另外两个主题:&/p&&p&Dark主题:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&map_out1&-remap(mapdata=map_data,title=&我是标题&,subtitle=&我是副标题&,theme =get_theme(theme=&Dark&))
plot(map_out1)
&/code&&/pre&&/div&&figure&&img src=&https://pic4.zhimg.com/v2-3ae89c1b4f7a53f6d230b_b.jpg& data-rawwidth=&1387& data-rawheight=&759& class=&origin_image zh-lightbox-thumb& width=&1387& data-original=&https://pic4.zhimg.com/v2-3ae89c1b4f7a53f6d230b_r.jpg&&&/figure&&p&Sky主题:&br&&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&map_out2&-remap(mapdata=map_data,title=&我是标题&,subtitle=&我是副标题&,theme =get_theme(theme=&Sky&))
&/code&&/pre&&/div&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&map_out&-remap(mapdata=map_data,
title=&我是标题&,
subtitle=&我是副标题&,
theme =get_theme(theme=&Bright&))
plot(map_out2)
&/code&&/pre&&/div&&figure&&img src=&https://pic3.zhimg.com/v2-d2fedb_b.jpg& data-rawwidth=&1387& data-rawheight=&759& class=&origin_image zh-lightbox-thumb& width=&1387& data-original=&https://pic3.zhimg.com/v2-d2fedb_r.jpg&&&/figure&&p&以上是该函数内置的三款主题,当然如果你要想自定义主题的话,也是可以的,但是需要将theme=&Sky&设置为none,否则系统主题会覆盖掉你自定义的主题。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span}

我要回帖

更多关于 lib文件怎么打开 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信