开发者博客 – IT技术 尽在开发者博客

开发者博客 – 科技是第一生产力


  • 首页

  • 归档

  • 搜索

Java基础项目实战--大学生求职招聘信息网站系统

发表于 2021-10-29

​本文正在参与 「掘力星计划」 ,赢取创作大礼包,挑战创作激励金。

临近学期结束,还是毕业设计,你还在做java期末作业、程序网络编程,不知道毕业设计该怎么办?老师的作业要求觉得大了吗?没有合适的类型或系统?网页功能的数量是否太多?等等。这里,你想解决的问题,在这里都能满足你的需求。原始Jsp、SSM、SpringBoot、以及HTML+CSS+JS页面设计, web大学生网页设计作业源码等等都可以参考得到解决。话不多说直接拿一个学生求职招聘管理系统来举例

视频演示:程序员私活挣钱—Java基础毕业项目实战-大学生求职招聘信息管理系统.mp4

摘要设计:

当前社会竞争日趋激烈,“找工作难”已成为社会的一大难题。问题的关键在于求职和招聘的人员都陷入了如何找到适合自己的工作以及如何招到合格的人才来填补公司空缺的这一矛盾中。在国内,网络招聘发展迅速。首先因为网上投递的简历比较多,而且应聘者素质较高。还有,网络招聘的优点是快捷,方便,招聘网站所提供的一些服务还可以帮助企业筛选简历,提高企业的工作效率。另外,网络招聘上花的成本远远低于传统的任何一种方式,这也是企业选择网络招聘的一个重要原因。

系统功能概述:

管理员: 登录注册、招聘信息的发布和管理、企业资料管理、推荐企业管理、求职技巧管理、系统简介管理、关于我们管理、留言板管理、工作地点管理、求职者信息管理、企业信息管理、个人信息管理和修改密码等具体功能设计

企业: 登录注册、招聘信息的发布和管理、企业资料管理、推荐企业查看、求职技巧查看、求职者信息查看、招聘信息查看、企业信息管理和修改密码等

普通用户: 登录注册、招聘信息查看、企业资料查看、推荐企业查看、求职技巧查看、求职者信息查看、招聘信息查看以及个人信息和修改等

主要功能截图: 主页获取源码联系

系统主页 :

)​ 招聘信息:

)​

)​

企业资料:

)​

企业推荐:

)​ 求职技巧:

)​

)​ 系统介绍:

)​

留言板:

)​

后台首页:后台功能太多就不一一截图了

)​

)“)​)​

主要代码展示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
java复制代码public class MainCtrl extends HttpServlet {

public MainCtrl() {
super();
}

public void destroy() {
super.destroy(); // Just puts "destroy" string in log
// Put your code here
}

public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.doPost(request, response);
}

public void go(String url,HttpServletRequest request, HttpServletResponse response)
{
try {
request.getRequestDispatcher(url).forward(request, response);
} catch (ServletException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}

public void gor(String url,HttpServletRequest request, HttpServletResponse response)
{
try {
response.sendRedirect(url);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}

public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
HttpSession session = request.getSession();
HashMap user = (HashMap)session.getAttribute("admin");
String ac = request.getParameter("ac");
if(ac==null)ac="";
CommDAO dao = new CommDAO();
String date = Info.getDateStr();
String today = date.substring(0,10);
String tomonth = date.substring(0,7);


if(ac.equals("mlogin"))
{
String username = request.getParameter("uname");
String password = request.getParameter("upass");
List<HashMap> list = dao
.select("select * from sysuser where uname='"
+ username + "'");
if (list.size() == 1) {
HashMap map = list.get(0);
List<HashMap> ulist = dao
.select("select * from sysuser where uname='"
+ username + "' and upass='" + password
+ "'");

if (ulist.size() == 1&& password.equals(map.get("upass").toString())) {

request.getSession().setAttribute("admin", map);

gor("/jsp_qzzp_sys/index.jsp", request, response);

} else {
request.setAttribute("error", "");
go("/index.jsp", request, response);
}
} else {
request.setAttribute("error", "");
go("/index.jsp", request, response);
}

}


//修改密码
if(ac.equals("uppass"))
{
String olduserpass = request.getParameter("olduserpass");
String userpass = request.getParameter("userpass");
String copyuserpass = request.getParameter("copyuserpass");
user = dao.getmap(Info.getUser(request).get("id").toString(), "sysuser");
if(!(((String)user.get("upass")).equals(olduserpass)))
{
request.setAttribute("error", "");
go("/admin/uppass.jsp", request, response);
}else{
String id = (String)user.get("id");
String sql = "update sysuser set upass='"+userpass+"' where id="+id;
dao.commOper(sql);
request.setAttribute("suc", "");
go("/admin/uppass.jsp", request, response);
}
}


if(ac.equals("uploaddoc"))
{
try {
String filename="";
request.setCharacterEncoding("utf-8");
RequestContext requestContext = new ServletRequestContext(request);
if(FileUpload.isMultipartContent(requestContext)){

DiskFileItemFactory factory = new DiskFileItemFactory();
factory.setRepository(new File(request.getRealPath("/upfile/")+"/"));
ServletFileUpload upload = new ServletFileUpload(factory);
upload.setSizeMax(100*1024*1024);
List items = new ArrayList();

items = upload.parseRequest(request);

FileItem fileItem = (FileItem) items.get(0);
if(fileItem.getName()!=null && fileItem.getSize()!=0)
{
if(fileItem.getName()!=null && fileItem.getSize()!=0){
File fullFile = new File(fileItem.getName());
filename = Info.generalFileName(fullFile.getName());
File newFile = new File(request.getRealPath("/upfile/")+"/" + filename);
try {
fileItem.write(newFile);
} catch (Exception e) {
e.printStackTrace();
}
}else{
}
}
}

go("/js/uploaddoc.jsp?docname="+filename, request, response);
} catch (Exception e1) {
e1.printStackTrace();
}
}


public void init() throws ServletException {
// Put your code here
}

public static void main(String[] args) {
System.out.println(new CommDAO().select("select * from mixinfo"));
}

}

主要数据库设计:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
sql复制代码CREATE TABLE IF NOT EXISTS `area` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`addr` varchar(255) DEFAULT NULL,
`delstatus` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=32 DEFAULT CHARSET=utf8;

CREATE TABLE IF NOT EXISTS `emp` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`uname` varchar(255) DEFAULT NULL,
`tname` varchar(255) DEFAULT NULL,
`title` varchar(255) DEFAULT NULL,
`savetime` varchar(255) DEFAULT NULL,
`status` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;


CREATE TABLE IF NOT EXISTS `fav` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`qzzuname` varchar(255) DEFAULT NULL,
`zpinfoid` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;


CREATE TABLE IF NOT EXISTS `hbnews` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(255) DEFAULT NULL,
`author` varchar(255) DEFAULT NULL,
`filename` varchar(255) DEFAULT NULL,
`content` text DEFAULT NULL,
`savetime` varchar(255) DEFAULT NULL,
`infotype` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

CREATE TABLE IF NOT EXISTS `messages` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`saver` varchar(255) DEFAULT NULL,
`savetime` varchar(255) DEFAULT NULL,
`content` varchar(255) DEFAULT NULL,
`resaver` varchar(255) DEFAULT NULL,
`recontent` varchar(255) DEFAULT NULL,
`resavetime` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;

CREATE TABLE IF NOT EXISTS `msg` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`content` text DEFAULT NULL,
`qzzuname` varchar(255) DEFAULT NULL,
`qyid` varchar(255) DEFAULT NULL,
`savetime` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;

CREATE TABLE IF NOT EXISTS `sysuser` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`uname` varchar(255) DEFAULT NULL,
`upass` varchar(255) DEFAULT NULL,
`tname` varchar(255) DEFAULT NULL,
`filename` varchar(255) DEFAULT NULL,
`sex` varchar(255) DEFAULT NULL,
`qq` varchar(255) DEFAULT NULL,
`email` varchar(255) DEFAULT NULL,
`houhold` varchar(255) DEFAULT NULL,
`addrs` varchar(255) DEFAULT NULL,
`birth` varchar(255) DEFAULT NULL,
`wei` varchar(255) DEFAULT NULL,
`hei` varchar(255) DEFAULT NULL,
`health` varchar(255) DEFAULT NULL,
`tel` varchar(255) DEFAULT NULL,
`school` varchar(255) DEFAULT NULL,
`zy` varchar(255) DEFAULT NULL,
`xl` varchar(255) DEFAULT NULL,
`cbdate` varchar(255) DEFAULT NULL,
`grjs` text DEFAULT NULL,
`grjl` varchar(255) DEFAULT NULL,
`xqah` varchar(255) DEFAULT NULL,
`gzjl` varchar(255) DEFAULT NULL,
`xmjy` varchar(255) DEFAULT NULL,
`wyll` varchar(255) DEFAULT NULL,
`utype` varchar(255) DEFAULT NULL,
`savetime` varchar(255) DEFAULT NULL,
`qzyx` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=24 DEFAULT CHARSET=utf8;

CREATE TABLE IF NOT EXISTS `yqlj` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`ljname` varchar(255) DEFAULT NULL,
`ljurl` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

CREATE TABLE IF NOT EXISTS `zdatadic` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`datatype` varchar(255) DEFAULT NULL,
`ptitle` varchar(255) DEFAULT NULL,
`content` text DEFAULT NULL,
`savetime` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8;

CREATE TABLE IF NOT EXISTS `zpinfo` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(255) DEFAULT NULL,
`zprs` varchar(255) DEFAULT NULL,
`gzdd` varchar(255) DEFAULT NULL,
`xl` varchar(255) DEFAULT NULL,
`jl` varchar(255) DEFAULT NULL,
`infotype` varchar(255) DEFAULT NULL,
`bei` varchar(255) DEFAULT NULL,
`dy` varchar(255) DEFAULT NULL,
`savetime` varchar(255) DEFAULT NULL,
`qyuname` varchar(255) DEFAULT NULL,
`qytname` varchar(255) DEFAULT NULL,
`gznr` text DEFAULT NULL,
`fl` text DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;

大家点赞、收藏、关注、评论啦 、
打卡 文章 更新 89/ 100天

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

不懂Mysql排序的特性,加班到12点,认了认了!

发表于 2021-10-29

小弟新写了一个功能,自测和测试环境测试都没问题,但在生产环境会出现偶发问题。于是,加班到12点一直排查问题,终于定位了的问题原因:Mysql Limit查询优化导致。现抽象出问题模型及解决方案,分析给大家,避免大家踩坑。

问题场景

新上线一个交易记录导出功能,逻辑很简单:根据查询条件,导出对应的数据。由于数据量比较大,在查询数据库时采用了分页查询,每次查询1000条数据。

自测正常,测试环境正常,上线之后运营反馈导出的数据有重复记录。

原本是以为业务逻辑问题,重新Review了一遍代码,依旧未找到问题原因。最后只好把SQL语句拿出来单独执行,导出数据,对比发现竟然是SQL语句查询结果乱序导致的。

原因分析

查询语句以create_time进行倒序排序,通过limit进行分页,在正常情况下不会出现问题。但当业务并发量比较大,导致create_time存在大量相同值时,再基于limit进行分页,就会出现乱序问题。

出现的场景是:以create_time排序,当create_time存在相同值,通过limit分页,导致分页数据乱序。

比如,查询1000条数据,其中有一批create_time记录值都为”2021-10-28 12:12:12“,当创建时间相同的这些数据,一部分出现在第一页,一部分出现在第二页,在查询第二页的数据时,可能会出现第一页已经查过的数据。

也就是说,数据会来回跳动,一会儿出现在第一页,一会儿出现在第二页,这就导致导出的数据一部分重复,一部分缺失。

查看了Mysql 5.7和8.0的官方文档,描述如下:

If multiple rows have identical values in the ORDER BY columns, the server is free to return those rows in any order, and may do so differently depending on the overall execution plan. In other words, the sort order of those rows is nondeterministic with respect to the nonordered columns.

上述内容概述:在使用ORDER BY对列进行排序时,如果对应(ORDER BY的列)列存在多行相同数据,(Mysql)服务器会按照任意顺序返回这些行,并且可能会根据整体执行计划以不同的方式返回。

简单来说就是:ORDER BY查询的数据,如果ORDER BY列存在多行相同数据,Mysql会随机返回。这就会导致虽然使用了排序,但也会发生乱序的状况。

解决方案

针对上述问题,基本的解决思路是:避免ORDER BY列的值出现重复。因此,可以加入其他维度,比如ID等其他排序列。

1
sql复制代码select * from tb_order order by create_time ,id desc;

这样,在create_time相同时,会根据id进行排序,而id肯定是不同的,就再不会出现上述问题了。

拓展知识

其实,上述内容在Mysql的官网已经有明确说明,而且还举了例子。下面对官网的内容和例子做一个简单的汇总总结。

limit查询优化

如果我们只是查询一个结果集的一部分,那么不要查询所有数据,然后再丢弃不需要的数据,而是要通过limit条件来进行限制。

在没使用having条件时,Mysql可能会对limit条件优化:

  • 如果只查询几条数据,建议使用limit,这样Mysql可能会用到索引,而通常情况下Mysql是全表扫描;
  • 如果将limit row_count和order by结合使用,Mysql会在找到第一个row_count结果集后立刻停止排序,而不是对整个结果集进行排序。如果此时基于索引进行操作,速度会更快。如果必须进行文件排序,在找到row_count结果集之前,会对部分或所有符合条件的结果进行排序。但当找到row_count结果之后,便不会对剩余部分进行排序了。这种特性的一个表现就是我们前面提到的带有limit和不带limit进行查询时,返回的结果顺序可能不同。
  • 如果将limit row_count和distinct结合使用,Mysql会在找到row_count结果集唯一行后立马停止。
  • 在某些情况下,可以通过按照顺序读取索引(或对索引进行排序),然后计算摘要直到索引变化来实现group by。在这种情况下,limit row_count不会计算任何不必要的group by值。
  • 一旦MySQL向客户端发送了所需数量的行,就会中止查询,除非使用了SQL_CALC_FOUND_ROWS。在这种情况下,可以使用 SELECT FOUND_ROWS() 检索行数。
  • LIMIT 0会快速返回一个空集合,通常可用于检查SQL的有效性。还可以用于在应用程序中获得结果集的类型。在Mysql客户端中,可以使用--column-type-info来显示结果列类型。
  • 如果使用临时表来解析查询,Mysql会使用 limit row_count来计算需要多少空间。
  • 如果order by未使用索引,且存在limit条件,则优化器可能会避免使用合并文件,而采用内存filesort操作对内存中的行进行排序。

了解了limit的一些特性,下面再回到本文的重点,limit row_count和order by结合使用特性。

limit与order by结合使用

在上面第二条中已经提到,limit row_count和order by结合呈现的特性之一就是结果返回的顺序是不确定的。而影响执行计划的一个因素就是limit,因此带有limit与不带有limit执行同样的查询语句,返回结果的顺序可能不同。

下面示例中,根据category列进行排序查询,而id和rating是不确定的:

1
2
3
4
5
6
7
8
9
10
11
12
sql复制代码mysql> SELECT * FROM ratings ORDER BY category;
+----+----------+--------+
| id | category | rating |
+----+----------+--------+
| 1 |       1 |   4.5 |
| 5 |       1 |   3.2 |
| 3 |       2 |   3.7 |
| 4 |       2 |   3.5 |
| 6 |       2 |   3.5 |
| 2 |       3 |   5.0 |
| 7 |       3 |   2.7 |
+----+----------+--------+

当查询语句包含limit时,可能会影响到category值相同的数据:

1
2
3
4
5
6
7
8
9
10
sql复制代码mysql> SELECT * FROM ratings ORDER BY category LIMIT 5;
+----+----------+--------+
| id | category | rating |
+----+----------+--------+
| 1 |       1 |   4.5 |
| 5 |       1 |   3.2 |
| 4 |       2 |   3.5 |
| 3 |       2 |   3.7 |
| 6 |       2 |   3.5 |
+----+----------+--------+

其中id为3和4的结果位置发生了变化。

在实践中,保持查询结果的顺序性往往非常重要,此时就需要引入其他列来保证结果的顺序性了。当上述实例引入id之后,查询语句及结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
sql复制代码mysql> SELECT * FROM ratings ORDER BY category, id;
+----+----------+--------+
| id | category | rating |
+----+----------+--------+
| 1 |       1 |   4.5 |
| 5 |       1 |   3.2 |
| 3 |       2 |   3.7 |
| 4 |       2 |   3.5 |
| 6 |       2 |   3.5 |
| 2 |       3 |   5.0 |
| 7 |       3 |   2.7 |
+----+----------+--------+
​
mysql> SELECT * FROM ratings ORDER BY category, id LIMIT 5;
+----+----------+--------+
| id | category | rating |
+----+----------+--------+
| 1 |       1 |   4.5 |
| 5 |       1 |   3.2 |
| 3 |       2 |   3.7 |
| 4 |       2 |   3.5 |
| 6 |       2 |   3.5 |
+----+----------+--------+

可以看出,当添加了id列的排序,即使category相同,也不会出现乱序问题。这正与我们最初的解决方案一致。

小结

本来通过实践中偶发的一个坑,聊到了Mysql对limit查询语句的优化,同时提供了解决方案,即满足了业务需求,又避免了业务逻辑的错误。

很多朋友都在使用order by和limit语句进行查询,但如果不知道Mysql的这些优化特性,很可能已经入坑,只不过数据量没有触发呈现而已。

如果这篇文章帮到你了,关注一波,后续更多实战干货分享。

Mysql官方文档:dev.mysql.com/doc/refman/…

博主简介:《SpringBoot技术内幕》技术图书作者,酷爱钻研技术,写技术干货文章。

公众号:「程序新视界」,博主的公众号,欢迎关注~

技术交流:请联系博主微信号:zhuan2quan

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

多图详解 分布式高可用性典型——Dynamo DB 分布式之

发表于 2021-10-29

本文已参与「掘力星计划」,赢取创作大礼包,挑战创作激励金。

分布式之DynamoDB

仔细思索分布式的CAP理论,就能发现P数据分区是分布式的特性,是必须满足的特点。如果一个系统没有数据分区的存在,那这样的系统就是单体,而不是分布式。CAP三个特性无法同时满足,那么所有的分布式系统实现都是在A(可用性)和C(一致性)之间权衡选择。

DynamoDB是Amazon的一个分布式的NOSQL数据系统,放弃了CAP的强一致性,保证高可用性。

DynamoDB是什么?

CAP只能保证满足AC中的一个,Dynamo的设计初衷是保证高可用,达到最终一致性。

在分布式的环境里,强一致性协议也有很多,比如Raft就是这样的一种协议。
但是Dynamo的目标不在于强一致性,可以认为Dynamo是完全去中心化的,满足AP,保证最终一致性。

Dynamo的设计是非常有意思的,他的独特的思想和实现给业界带来了很大的冲击。

Dynamo数据分布模型:一致性哈希

在分布式系统里,数据不是存放在一个单体里面,而是需要均匀安全的存放在非常多的节点上面。数据怎么分布,是分布式系统要考虑的关键问题之一。

Dynamo的数据分布(数据分片,也叫分区)使用的模型是一致性哈希模型。

一致性哈希是什么

哈希环的表示如下所示,用一个环表示0-2^32-1之间的范围。

hash1.png

数据分布的每个节点对应一个随机值,是环上的某个点。按照一致性方向,哈希环上的一段区间都映射到这个节点上面来(也就是说这段数据将会存储到这个节点)。

ring0.png

但是这样可能导致数据分布不均匀,负载量小的机器可能分配到很大的一片数值空间,负载量大的机器反而可能分配到很小的一片数值空间。

虚拟节点

为了更好的利用物理机器的异构性,一致性哈希提出了虚拟节点的设计思路:一个机器分配N个随机值(N的大小跟机器的能力有关系),对应哈希环上的N个节点,如下图所示。

hash2.png
Tij表示i机器的第j个虚拟节点,这个Hash Ringm目前有两个节点T1和T2,T1有三个虚拟节点T11、T12和T13,T2有两个虚拟节点T21和T22 。假设他们按照这种方式分布在环上。

全部数值区间按照一致的方向去映射,
比如T11对应的数值和T21对应数值之间的那一段的哈希值映射到虚拟节点T21上面。

Dynamo是一个纯粹的KV存储系统,这样的话每个key都会计算出一个哈希值,并存储到对应的虚拟节点上面(对应某一台机器)。

初始环

理解初始环的建立过程是虚拟节点建立一致性哈希的关键,建立过程如下图:
hash3.png

  • T1初始化作为种子节点,生成【0,2^32-1】范围内的三个随机数,随机数对应T11、T12和T13。
  • T2初始化,和种子节点联通,生成并上报自己生成的的random值序列T21、T22。
  • 集群机器通过Gossip协议交换信息,这样大家都知道全局的虚拟节点地图了,也就是这个Hash Ring的分布图。

初始化的过程需要设置种子节点,各个节点通过gossip协议获得全局的哈希地图。
之后按照各自负责的区域,来处理数据请求并分片存储,以及用相应的副本保证高可用性。

新的节点加入

有新的Node T3加入到系统,
T3初始化,根据自己的存储和计算
能力生成两个随机值T31和T32,
并上报给种子节点T1,也从T1
获得现有的hash ring的地图,
于是所有节点都获得新的地图

hash5.png
hash ring 的部分hash区间
映射到的节点发生变化,
对全局影响不大。
T12-T31之间的区间range1
原来是映射到T13,
需要从T13搬移到T31。另外一个虚拟节点那里也是类似的。

这体现了一致性哈希的好处,新增节点对数据搬移的开销小。

有节点离开

如下图,Node T2离开系统,
则需要先迁移数据。

hash4.png

  • T11-T21之间的区间range3
    原来是映射到T21,
    需要从T21搬移
    这区间的数据到T12
  • T13-T22之间的区间range
    原来是映射到T22,
    需要从T22搬移到T32
    最后,移除T2的虚拟节点,
    生成新的地图。

可以看出,迁移数据的代价也很小。这也体现了一致性哈希的好处,移除节点对数据搬移的开销小。

gossip协议

gossip协议很好懂,就是随机选择节点并传播通信交换数据,也很好实现,而且很快就可以模拟一个来了解这块的细节。

数据分片和副本

在这个数据分布的一致性哈希模型下,哈希区间自然的将数据分片存储。如果没有多副本,系统很难保证高可用性。

副本是保证数据高可用的保证,但也需要保证各个副本之间的一致性,总之是带来了更大的系统开销。

Dynamo副本的存储如规则下:

dynamo2.png

假设系统设置的副本数N=3,则T21需要存储的数据包括range1,range2和range3。

一致性哈希总结

  • 学习一致性哈希
  • 分布式存储的基础
  • 多台机器存数据
  • 数据分片可扩张
  • 多个备份能可靠
  • 每个节点一范围
  • 真实节点多虚拟
  • 均匀散落哈希环
  • 手拉手围一个环
  • 数据存取也不难
  • 哈希取模来映射
  • 指定节点来存储
  • 这些数据的备份
  • 在接下来的节点
  • 机器宕机也不怕
  • 数据转移且均匀
  • 节点心跳来交流
  • Gossip协议得全局

请求处理流程

DynamoDB系统是一个KV系统,客户端的请求很简单,基本是两类:PUT和GET。

PUT的时候考虑可用性和一致性的问题:

  • 可用性就是说要多副本存储
  • 一致性,数据一致性,多副本并发写能否保持一致

GET的时候也要考虑可用性和一致性的问题:

  • 可用性就是说要选择一个可用的节点返回需要的数据?
  • 一致性,数据一致性,如果有多个可用的数据,怎么合并解决冲突?

技术没有银弹,AC不可得兼。

可以发现所有的考量都是围绕可用性和一致性,下面详细说说这些过程和要考虑的一些问题。

put数据的处理流程?

客户端发送请求时,会选择协调模式。有两种:

  • 客户端SDK协调
  • 使用负载均衡服务器协调

不管是客户端协调还是负载均衡的协调,都要先选择一个节点作为主要负责put请求的逻辑,这个节点如果不可达,就换下一个可用的节点。

PUT.png

基本步骤:

  • SDK获取集群metadata信息,选择一个处理请求的节点。
  • 处理请求的节点计算hash(key),如图,key和值要存储到T13,
    并且复制到T31和T12,所以处理节点将请求发给这三个节点,T1是处理节点,发送put指令给T13、T31和T12三个节点。
  • 假设设置的W=2,则等待至少有一个replica复制成功才返回response给客户端。

当然在使用了Merkle Tree(后面会讲到这是优化一致性的一个数据结构)的情况下,会重新计算这棵树。

get数据的处理流程

同理,不管是客户端协调还是负载均衡的协调,都要先选择一个节点作为主要负责get的逻辑。

如图,这里使用负载均衡服务器

GET.png

GET的时候,设置读R个节点可返回。

get(key=100):

  • 计算出key在T13,副本在T31和T12,处理节点T1给这些节点发送get指令
  • 假设设置的R=2,则复制至少等这三个replica的其中两返回数据
  • 根据策略合并结果返回response给客户端或直接返回全部让客户端去处理

向量时钟

Dynamo 使用向量时钟(vector clock)来跟踪同一对象不同版本之间的因果性。

一个向量时钟关联了一个对象的所有版本,可以通过它来判断对象的两个版本是否在并行的分支上,或者它们是否有因果关系。
如果对象的第一个时钟上的所有 counter 都小于它的第二个时钟上的 counter,那第一个 时钟就是第二的祖先,可以安全的删除;否则,这两个修改就是有冲突的,需要 reconciliation。

一个向量时钟就是一个 (node, counter) 列表。node表示处理数据的节点,如上面GET、PUT流程中说到的T1。

下图显示了一个场景:

在T1故障的时候,T2和T3可能都写入了数据,之后T1恢复解决冲突数据。

vector.png

  • D1、D2..表示vector clocks,[T2,1]表示T2处理的第一个版本
  • 出现冲突写:D3([T1,2],[T2,1])和D4([T1,2],[T3,1])
  • 再下次client get数据的时候,假设T1恢复,由T1处理冲突,得到的新的版本是3
  • 冲突处理且删除D3([T1,2],[T2,1])和D4([T1,2],[T3,1])的数据,得到D5([T1,3],[T2,1],[t3,1])的数据

一致性检测的Merkle tree数据结构

为什么想知道replica data 数据是否一致?那是必须要有的,分布式系统至少保证起码的已一致性,也就是最终一致性。

可能系统会有巡检,看看各副本是不是都一致。因为可能有些更新丢失了,或者磁盘内存硬件异常导致的数据错误。

以及一般的通用的数据处理过程

  • put:写数据,同时复制数据到W个replcias
  • get:读数据从R个replica中去读
    Dynamo使用Merkle tree这种数据结构来比较副本之间的一致性,这是一种逆熵(anti entropy)的思想,如下图所示:

mekle.png

DynamoDB的每个节点
为每个数据分区都
维护了一个Merle Tree,通过Root的hash值就可以
验证同一片数据是否相同,
则不用比较所有数据。如果相等,则认为两个副本完全一样,如果不相等,则递归往下看哪里不相等,移动数据解决冲突。

分布式环境的故障检测和宕机处理

故障检测是各个节点之间gossip出来的,一个节点push数据的时候如果联不通就认为它peer不可用,如果大家就这个事情达成一致,则整体认为这个节点不可用,然后将数据转移,需要把上面的数据分片均匀分配给相邻的几个节点。

乐观多版本处理

上面已经提到了,是数据高可用性保证的处理过程中的一个思想。

参考文献

  • arthurchiao.art/blog/amazon…
  • www.allthingsdistributed.com/files/amazo…

「欢迎在评论区讨论,掘金官方将在掘力星计划活动结束后,在评论区抽送100份掘金周边,抽奖详情见活动文章」。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

Doris

发表于 2021-10-28

Doris:Apache Doris是一个现代化的MPP分析型数据库产品。仅需亚秒级响应时间即可获得查询结果,有效地支持实时数据分析。Apache Doris的分布式架构非常简洁,易于运维,并且可以支持10PB以上的超大数据集。Apache Doris可以满足多种数据分析需求,例如固定历史报表,实时数据分析,交互式数据分析和探索式数据分析等。令您的数据分析工作更加简单高效!

关键词:MPP、亚秒级、大数据量、高效的数据分析

OLAP&&ALTP

  1. OLTP:主要用来记录某类业务事件的发生,如购买行为,当行为产生后,系统会记录是谁在何时何地做了何事,这样的一行(或多行)数据会以增删改的方式在数据库中进行数据的更新处理操作,要求实时性高、稳定性强、确保数据及时更新成功
  2. OLAP:当数据积累到一定的程度,我们需要对过去发生的事情做一个总结分析时,就需要把过去一段时间内产生的数据拿出来进行统计分析,从中获取我们想要的信息,为公司做决策提供支持,这时候就是在做OLAP了。

因为OLTP所产生的业务数据分散在不同的业务系统中,而OLAP往往需要将不同的业务数据集中到一起进行统一综合的分析,这时候就需要根据业务分析需求做对应的数据清洗后存储在数据仓库中,然后由数据仓库来统一提供OLAP分析。所以我们常说OLTP是数据库的应用,OLAP是数据仓库的应用,下面用一张图来简要对比。

image.png

Doris的特点

  1. 现代化MPP架构:

1.1 概念:即messively parallel processing,大规模并行处理。通常来说,就是将任务分发给不同的节点进行并行计算,最终将结果汇总返回。

1.2 架构特点:

* 多节点:节点间数据不共享,只能通过网络协同。通信效率一定程度上决定了查询效率
* 节点独立:每一个节点都有自己的CPU、磁盘、总线
* 并行计算:数据根据hash算法分散到不同的服务器上,在任务执行时也是分散到不同的机器上执行完成后汇总返回
* CAP:首先保证C(一致性),然后保证A(可用性),最后考虑P(分区容错性)。1.3 架构特点带来的劣势:


* 依赖网络:集群机器协同工作依赖网络,一旦网络出错将导致不可用,
* 扩展性差:业务数据分配到了不同的节点,一旦节点过多,元数据管理将十分困难
  1. 亚秒级的查询时延:任务分发至不同节点并行处理,提高了查询效率。
  2. 兼容SQL协议:支持标准SQL语言,兼容MySQL协议
  3. 向量化执行器

4.1 向量化执行引擎可以减少节点间的调度,提高CPU的利用率。

4.2 因为列存数据,同一列的数据放在一起,导致向量化执行引擎在执行的时候拥有了更多的机会能够利用的当前硬件与编译的新优化特征。

4.3 因为列存数据存储将同类型的类似数据放在一起使得压缩比能够达到更高,这样可以拉近一些磁盘IO能力与计算能力的差距。
5. 高效的聚合表技术:用户最终查询到的都是聚合后的数据。

5.1 数据聚合的三个阶段:

* ETL阶段:导入一批次的数据到目的端时,即 Extra(导入)->Transform(转换)->load(存储)的过程中
* Compaction阶段:底层在对数据进行Compaction时
* 查询:用户执行查询时
  1. 新型预聚合技术Rollup:对于查询会有部分聚合的处理动作,可以参考MySQL的WITH ROLLUP语法。
  2. 高性能、高可用、高可靠

Doris的架构:

应用程序服务器:即调用方,对Doris发起请求的服务器。

数据存储服务器:用于响应调用方的请求

管理中心服务器:是一个小型的服务集群,一半是由两台服务器组成,主要用于监控数据存储服务器的健康状态。

一般来说,数据存储服务器的分片、副本数越多,集群可用性就越高
8. 极简运维,弹性伸缩

挑战

  1. 数据类型是否能够结构化:mpp类型的架构适合处理PB级的结构化数据,那么业务数据的类型是否能结构化?
  2. Doris的数据导入问题:Doris支持的数据导入方式,基本能覆盖kafka、hdfs、mysql等作为数据上游。但是从官方的介绍来看,doris的数据导入是有一个弊端的,就是导入数据的部分异常,可能会导致整个任务的失败。Doris市面相关资料不多,使用学习和运维成本高、风险大。出了问题,比较难维护需要熟悉源码,才能去调试 bug 。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

shell命令的展开

发表于 2021-10-28

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

每当我们在终端中输入一些命令都会熟悉的按下enter键来执行,其实在按下按键之后会发生很多事情,首先这个过程叫展开,可能展开的结果和你的输入甚至已经完全不同,但这一切都会给用户呈现,只会显示执行中的炫酷过程。我们可以通过echo命令还观察命令是如何展开的,echo是shell的内置命令,只干一件事:打印跟在它后面的参数。

比如这样:

1
2
bash复制代码➜  ~ echo hello world
hello world

再看下面的结果:

image.png
这里echo *并没有无脑的输出*,是不是很奇怪。因为* 代表通配符,在当前目录下就匹配了所有的文件名,通配符所依赖的工作机制叫做路径名展开,类似于ls的功能。

再来,可以做一些别的展开:

1
2
bash复制代码➜  / echo u*
usr

这里匹配的就是以u开头的所有文件。我们知道~对于Linux系统的意义,这里代表了home的目录,就像下面的执行结果一样,它就是代表home:

1
2
bash复制代码➜  / echo ~
/Users/user

说了目录,shell也可以计算算数表达式,比如:

1
2
bash复制代码➜  / echo 2+2
2+2

似乎不太对,怎么没有计算结果呢?当然没有,命令行可没那么智能,是需要按照格式来写的:$((expression)),上面的命令改成echo $((2+2))即可。此外,也能支持列表遍历,不过日常场景下没多大用,使用{A..Z}这样的形式来表示:

1
2
bash复制代码➜  / echo test{A..D}
testA testB testC testD

其实最常用的展开方式还是读取系统变量,如果不存在这个变量,会输出空格,举例如下:

1
2
bash复制代码➜  / echo $JAVA_HOME
/Library/Java/JavaVirtualMachines/jdk-11.0.7.jdk/Contents/Home

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

SpringBoot集成MyBatis-Plus

发表于 2021-10-28

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

前言

MyBatis-Plus (opens new window)(简称 MP)是一个 MyBatis (opens new window)的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

确实MyBatis-Plus用起来比较爽的,没有了之前繁琐的xml文件的配置,和现在倡导的DDD领域驱动设计和单表操作,可以说是完美契合。

参考学习指南

官方文档:mp.baomidou.com/guide/

GITHUB地址: Gitee (opens new window) | Github(opens new window)

使用入门

POM依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
xml复制代码<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

<!-- mybaits plus 插件 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.0.5</version>
</dependency>

<!-- 阿里数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>

<!-- lombok简化代码 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>

数据库配置

1
2
3
4
5
6
7
8
9
10
11
yml复制代码spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/smp?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true
username: root
password: 1qaz!QAZ
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
serialization:
write-dates-as-timestamps: false

代码分层样例

image.png

DownLinkRecordMapper

1
2
3
4
5
java复制代码import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.sharding.jdbc.demo.entity.DownLinkRecord;

public interface DownLinkRecordMapper extends BaseMapper<DownLinkRecord> {
}

DownLinkRecord

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
java复制代码@Data
@TableName("smp_down_link_record")
public class DownLinkRecord {

@TableId(type = IdType.AUTO)
private Integer id;

private Integer merchantId;

private Integer massTaskId;

private String massTaskName;

private String phone;

private Integer status;

private String errorCode;

private Date createTime;
}

DownLinkRecordDAO

1
2
3
java复制代码@Repository
public class DownLinkRecordDAO extends ServiceImpl<DownLinkRecordMapper, DownLinkRecord> {
}

image.png

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

SpringBoot项目快速整合Quartz

发表于 2021-10-28

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动

SpringBoot整合Quartz时可以将该框架在SpringBoot框架基础上使用

  1. 依赖引入

在SpringBoot项目中使用Quartz是需要引入相关的依赖信息的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
xml复制代码<!--quartz依赖-->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.1</version>
</dependency>
<!--SpringBoot中Scheduled坐标-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<!--Spring tx 坐标-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</dependency>
  1. 步骤流程

  1. 定义任务类实现quertz包的Job接口,并实现其中execute()方法,定义任务内容
  2. 定义quertz相关配置类,创建JobDetailFactoryBean、SimpleTriggerFactoryBean、SchedulerFactoryBean对象并使用@Bean注入Spring容器中管理
    • 工厂类是SpringBoot提供的,工厂类存在于spring-context-support依赖的org.springframework.scheduling.quartz包中,定义创建相关对象时需要引入相依赖
    • JobDetailFactoryBean定义时需要设置定义的任务类
    • SimpleTriggerFactoryBean定义时需要传入JobDetailFactoryBean对象并配置触发策略
    • SchedulerFactoryBean定义时需要传入SimpleTriggerFactoryBean对象
  3. 在启动类上使用@EnableScheduing注解表示开启SpringBoot定时任务
  4. 启动服务,定时任务会按照定义策略执行
    • 启动服务时可能会报找不到类的错误,此时查看是否引入spring-tx依赖
  1. 示例代码

3.1 定义任务类

任务类仍然是要实现Job,并实现其中的execute接口来定义任务内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
java复制代码public class DoTaskTest implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
Long startTime = System.currentTimeMillis();
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
Thread.sleep(2000);
Long endTime = System.currentTimeMillis();
System.out.println("执行任务,执行时间:" + format.format(new Date(startTime)) + ",耗时:" + (endTime-startTime));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

3.2 配置类

在SpringBoot中使用时定义的配置类,可以将任务对象、触发器和调度器都交给Spring容器进行管理,在需要时取出来使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
java复制代码@Configuration
public class QuartzConfig {
/**
* ① 创建Job对象
*/
@Bean
public JobDetailFactoryBean jobDetailFactoryBean(){
JobDetailFactoryBean factoryBean = new JobDetailFactoryBean();
factoryBean.setJobClass(DoTaskTest.class);
return factoryBean;
}

/**
* 创建Trigger对象
*/
@Bean
public SimpleTriggerFactoryBean simpleTriggerFactoryBean(JobDetailFactoryBean jobDetailFactoryBean){
SimpleTriggerFactoryBean factoryBean = new SimpleTriggerFactoryBean();
factoryBean.setJobDetail(jobDetailFactoryBean.getObject());
factoryBean.setRepeatCount(10);
factoryBean.setStartDelay(5000);
factoryBean.setRepeatInterval(2000);
return factoryBean;
}

/**
* 创建Scheduler对象
*/
@Bean
public SchedulerFactoryBean schedulerFactoryBean(SimpleTriggerFactoryBean simpleTriggerFactoryBean){
SchedulerFactoryBean factoryBean = new SchedulerFactoryBean();
factoryBean.setTriggers(simpleTriggerFactoryBean.getObject());
return factoryBean;
}
}

3.3 启动类

@EnableScheduling注解用来开启定时任务,可以标注在启动类或者Quartz的配置类上。

1
2
3
4
5
6
7
8
9
java复制代码@SpringBootApplication
@EnableScheduling
public class OctoberApplication {

public static void main(String[] args) {
SpringApplication.run(OctoberApplication.class, args);
}

}
  1. 总结

在SpringBoot项目中整合Quartz还是比较容易的,这样在项目启动时就会自动执行任务类中的任务。

但是,对于实际的使用场景,定时任务肯定是不止一个的,这种情况下又该如何来定义任务、动态的实现触发器配置以及启停调度器呢。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

Windows系统安装MySQL80(20211013

发表于 2021-10-28

一、下载MySQL

MySQL官网下载地址

image-20211013105421351

二、解压并配置MySQL环境变量

将 mysql-8.0.25-winx64.zip 包解压,解压后的目录结构如下:

image-20211013111230497

找到环境变量,新建,变量名是 MYSQL_HOME ,变量值是解压后的路径。

image-20211013111732632

然后再找到Path,新建一个值 %MYSQL_HOME%\bin 。

image-20211013142024113

打开 cmd,输入 mysql,只要不提示 mysql 不是内部命令,就证明环境变量配置成功。

image-20211013143434937

三、创建 my.ini 配置文件

在解压根目录下创建 my.ini 文件,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
ini复制代码[mysq1d]
# 设置3306端口
port=3306
​
# 设置mysq1的安装目录
basedir=C:\Software\mysql-8.0.25-winx64
​
# 设置mysq1数据库的数据的存放目录
datadir=C:\Software\mysql-8.0.25-winx64\data
​
# 允许最大连接数
max_connections=200
​
# 允许连接失败的次数 这是为了防止有人从该主机试图攻击数据库系统
max_connect_errors=10
​
# 服务端使用的字符集默认为UTF8
character-set-server=UTF8MB4
​
# 创建新表时将使用的默认存储引擎
default-storage-engine=INNODB
​
# 默认使用“mysq1_native_password"插件认证
default_authentication_plugin=mysql_native_password
​
[mysq1]
# 设置mysq1客户端默认字符集
default-character-set=utf8mb4
​
[client]
# 设置mysql客户端连接服务端时默认使用的端口
port=3306
default-character-set=utf8mb4
​

四、安装MySQL

注意:以下操作必须以管理员身份执行

  • 初始化MySQL
1
ini复制代码mysqld --defaults-file=C:\Software\mysql-8.0.25-winx64\my.ini --initialize --console

image-20211013154901729

初始化成功后,会生成data目录以及临时密码。

  • 安装MySQL服务
1
css复制代码mysqld --install

安装成功后,打开 services.msc,可以看到本地MySQL服务。

image-20211013151757918

  • 启动MySQL服务
1
sql复制代码net start mysql

此时会报错,安装服务路径不对。正确的方式是:要在MySQL目录bin下安装服务。

使用命令 mysqld --remove 将已安装的服务删除,切换到bin下,重新安装。

image-20211013154340994

五、登录MySQL

  • 登录并修改密码
1
css复制代码mysql -u root -p

使用临时密码登录。登录后使用SQL修改密码。

1
sql复制代码alter user root@localhost identified by '123456@abc';
  • 开启远程访问
1
2
3
4
sql复制代码create user 'root'@'%' identified by '123456@abc'; --这一步执行失败没关系
grant all on *.* to 'root'@'%';
alter user 'root'@'%' identified with mysql_native_password by '123456@abc';
flush privileges;

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

python中使用生成器 生成器

发表于 2021-10-28

生成器

通过列表,可以包含很多很多的元素,但是受到内存的制约,列表的内容一定是有限的。它无法表达无限的内容。比如说,全部的正整数,就不可能被放到一个列表之中去使用。但是,全部的正整数确实是应当可以被用作使用的,因为只需要通过前一个元素,就能够推断出后一个元素了。

因此,在这种情况下,我们有了一种新的东西,就是生成器,也就是generator。生成器无需一下子保存或读取出全部的内容,只需要在需要用到的时候,生成一个,就可以了。

生成器生成式

之前,我们提到了列表推导式,实际上,只要将列表推导式中的[]改为(),就可以创建一个生成器了。如果模仿列表生成式或者列表推导式的名字的话,也许我们应当称之为生成器生成式或者生成器推导式,不过这种说法似乎不是很常见。

注:关于列表推导式,有的时候我称呼其为列表推导式,有的时候我又称呼其为列表生成式,是因为这两种称呼非常通用,使用哪一种都是一样的,所以,就没有为了统一性,而选择只使用其中的一种说法。如果你觉得哪一种说法更好的话,可以自行使用其中的一种。

比如说,之前我们写过这样一个列表生成式,我们将其改为生成器生成式。

1
2
3
4
5
6
7
8
python复制代码# 这是之前使用过的一个关于九九乘法表的列表推导式
li = ["%s * %s = %s" % (i, j, i * j) for i in range(1, 10) for j in range(1, 10) if i <= j]

# 将[]改为()以后,可以直接变成生成器
g_li = ("%s * %s = %s" % (i, j, i * j) for i in range(1, 10) for j in range(1, 10) if i <= j)

# 打印以后,我们可以看出,这是一个generator
print(g_li)

我们可以发现,直接打印一个生成器,是不可以的,因为它是在使用的过程中,才被生成的,因此,我们必须通过next()或者for循环的形式,去使用一个生成器。

1
2
3
4
5
6
7
8
9
python复制代码# 每使用一次next(),就会新生成一个元素
print(next(g_li))
print(next(g_li))
print(next(g_li))
print(next(g_li))

# 使用for循环,可以通过遍历得到所有的元素
for i in g_li:
print(i)

创建详细的生成器

虽然通过生成器生成式已经可以创建出生成器,但是,生成式的方式毕竟是一种比较简单的方式,面对一些复杂的需求的话,生成器难以应对。因此,我们可以通过函数定义的形式,得到一个生成器。

这里,我们需要使用一个关键字yield,当函数执行到yield时,会得到yield的内容,并且停止运行,当下一次调用的时候,会从上一次的yield开始继续运行,并执行到下一次yield再次暂停。

比如说,定义一个简单的生成器

1
2
3
4
5
6
python复制代码# 通过函数,定义生成器
def g_num(x):
for i in range(x):
# 当执行到yield i的时候,会生成i,并且暂停
# 当下一次调用的时候,会从这里继续运行,到下一个yield再次暂停
yield i

注意,此时g_num不是一个生成器,g_num是一个函数。当这个函数被调用的时候,我们得到的是一个生成器。我们可以像这样使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
python复制代码# 生成器的使用
for i in g_num(100):
print(i)

# 如果你不希望使用生成器,也可以通过list()
# 以此来将生成器直接转换为一个列表
a = list(g_num(10))

# 但是需要注意的是,不是所有的生成器都能够被转换为列表的
# 比如以下代码中,生成器是可以直接获取到的,但是如果你要将其转换为列表
# 可能就会失败或者使计算机陷入一些困境之中
x1 = g_num(1000000000000)
x2 = list(g_num(1000000000000))

总结

虽然通过next()或者for循环两种方式都可以使用生成器,但是一般来说,不会真的手动通过next()调用的。使用for循环来配合生成器使用要更多一些。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

怎么解决javaioIOException 拒绝访问/

发表于 2021-10-28

######目录

  • 问题:用Java代码在c盘创建文件对象,运行显示java.io.IOException:拒绝访问
  • 踩得坑:更改磁盘权限,结果运行报错:java.io.IOException: 客户端没有所需的特权
  • 解决方法:换个磁盘创建文件:将c盘改成f盘,运行成功并在f盘创建文件

###问题:用Java代码在c盘创建文件对象,运行显示java.io.IOException:拒绝访问

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
java复制代码package org.zhaiyujia.pkg1;

import java.io.File;
import java.io.IOException;

public class TestFile {
File file;
public TestFile() {
file=new File("c://a.txt");
boolean b=file.exists();
if(!b) { //之前报错是因为要强制try{}catch{}
try {
System.out.println(file.createNewFile());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static void main(String[] args) {
new TestFile();
}

}

这里写图片描述
###踩得坑:更改磁盘权限,结果运行报错:java.io.IOException: 客户端没有所需的特权这里写图片描述)这里写图片描述
###解决方法:换个磁盘创建文件:将c盘改成f盘,运行成功并在f盘创建文件这里写图片描述

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

1…458459460…956

开发者博客

9558 日志
1953 标签
RSS
© 2025 开发者博客
本站总访问量次
由 Hexo 强力驱动
|
主题 — NexT.Muse v5.1.4
0%