Spark数据倾斜解决方案(转)

本文转发自技术世界,原文链接 http://www.jasongj.com/spark/skew/

Spark性能优化之道——解决Spark数据倾斜(Data Skew)的N种姿势

自定义Partitioner

原理

使用自定义的Partitioner(默认为HashPartitioner),将原本被分配到同一个Task的不同Key分配到不同Task。

案例

以上述数据集为例,继续将并发度设置为12,但是在groupByKey算子上,使用自定义的Partitioner(实现如下)

 1 .groupByKey(new Partitioner() {
 2 @Override
 3 public int numPartitions() {
 4 return 12;
 5 }
 6  
 7 @Override
 8 public int getPartition(Object key) {
 9 int id = Integer.parseInt(key.toString());
10 if(id >= 9500000 && id <= 9500084 && ((id - 9500000) % 12) == 0) {
11 return (id - 9500000) / 12;
12 } else {
13 return id % 12;
14 }
15 }
16 })

由下图可见,使用自定义Partition后,耗时最长的Task 6处理约1000万条数据,用时15秒。并且各Task所处理的数据集大小相当。 

customizec partitioner

总结

适用场景
大量不同的Key被分配到了相同的Task造成该Task数据量过大。

解决方案
使用自定义的Partitioner实现类代替默认的HashPartitioner,尽量将所有不同的Key均匀分配到不同的Task中。

优势
不影响原有的并行度设计。如果改变并行度,后续Stage的并行度也会默认改变,可能会影响后续Stage。

劣势
适用场景有限,只能将不同Key分散开,对于同一Key对应数据集非常大的场景不适用。效果与调整并行度类似,只能缓解数据倾斜而不能完全消除数据倾斜。而且需要根据数据特点自定义专用的Partitioner,不够灵活。

将Reduce side Join转变为Map side Join

原理

通过Spark的Broadcast机制,将Reduce侧Join转化为Map侧Join,避免Shuffle从而完全消除Shuffle带来的数据倾斜。
spark map join

案例

通过如下SQL创建一张具有倾斜Key且总记录数为1.5亿的大表test。

1 INSERT OVERWRITE TABLE test
2 SELECT CAST(CASE WHEN id < 980000000 THEN (95000000 + (CAST (RAND() * 4 AS INT) + 1) * 48 )
3 ELSE CAST(id/10 AS INT) END AS STRING),
4 name
5 FROM student_external
6 WHERE id BETWEEN 900000000 AND 1050000000;

使用如下SQL创建一张数据分布均匀且总记录数为50万的小表test_new。 

1 INSERT OVERWRITE TABLE test_new
2 SELECT CAST(CAST(id/10 AS INT) AS STRING),
3 name
4 FROM student_delta_external
5 WHERE id BETWEEN 950000000 AND 950500000;


直接通过Spark Thrift Server提交如下SQL将表test与表test_new进行Join并将Join结果存于表test_join中。
 

1 INSERT OVERWRITE TABLE test_join
2 SELECT test_new.id, test_new.name
3 FROM test
4 JOIN test_new
5 ON test.id = test_new.id;

该SQL对应的DAG如下图所示。从该图可见,该执行过程总共分为三个Stage,前两个用于从Hive中读取数据,同时二者进行Shuffle,通过最后一个Stage进行Join并将结果写入表test_join中。 

reduce join DAG

从下图可见,Join Stage各Task处理的数据倾斜严重,处理数据量最大的Task耗时7.1分钟,远高于其它无数据倾斜的Task约2秒的耗时。
reduce join DAG

接下来,尝试通过Broadcast实现Map侧Join。实现Map侧Join的方法,并非直接通过CACHE TABLE test_new将小表test_new进行cache。现通过如下SQL进行Join。

1 CACHE TABLE test_new;
2 INSERT OVERWRITE TABLE test_join
3 SELECT test_new.id, test_new.name
4 FROM test
5 JOIN test_new
6 ON test.id = test_new.id;


通过如下DAG图可见,该操作仍分为三个Stage,且仍然有Shuffle存在,唯一不同的是,小表的读取不再直接扫描Hive表,而是扫描内存中缓存的表。
 

reduce join DAG

并且数据倾斜仍然存在。如下图所示,最慢的Task耗时为7.1分钟,远高于其它Task的约2秒。
reduce join DAG

正确的使用Broadcast实现Map侧Join的方式是,通过SET spark.sql.autoBroadcastJoinThreshold=104857600;将Broadcast的阈值设置得足够大。

再次通过如下SQL进行Join。

1 SET spark.sql.autoBroadcastJoinThreshold=104857600;
2 INSERT OVERWRITE TABLE test_join
3 SELECT test_new.id, test_new.name
4 FROM test
5 JOIN test_new
6 ON test.id = test_new.id;

通过如下DAG图可见,该方案只包含一个Stage。 

reduce join DAG

并且从下图可见,各Task耗时相当,无明显数据倾斜现象。并且总耗时为1.5分钟,远低于Reduce侧Join的7.3分钟。
reduce join DAG

总结

适用场景
参与Join的一边数据集足够小,可被加载进Driver并通过Broadcast方法广播到各个Executor中。

解决方案
在Java/Scala代码中将小数据集数据拉取到Driver,然后通过Broadcast方案将小数据集的数据广播到各Executor。或者在使用SQL前,将Broadcast的阈值调整得足够大,从而使用Broadcast生效。进而将Reduce侧Join替换为Map侧Join。

优势
避免了Shuffle,彻底消除了数据倾斜产生的条件,可极大提升性能。

劣势
要求参与Join的一侧数据集足够小,并且主要适用于Join的场景,不适合聚合的场景,适用条件有限。

为skew的key增加随机前/后缀

原理

为数据量特别大的Key增加随机前/后缀,使得原来Key相同的数据变为Key不相同的数据,从而使倾斜的数据集分散到不同的Task中,彻底解决数据倾斜问题。Join另一则的数据中,与倾斜Key对应的部分数据,与随机前缀集作笛卡尔乘积,从而保证无论数据倾斜侧倾斜Key如何加前缀,都能与之正常Join。
spark random prefix

案例

通过如下SQL,将id为9亿到9.08亿共800万条数据的id转为9500048或者9500096,其它数据的id除以100取整。从而该数据集中,id为9500048和9500096的数据各400万,其它id对应的数据记录数均为100条。这些数据存于名为test的表中。

对于另外一张小表test_new,取出50万条数据,并将id(递增且唯一)除以100取整,使得所有id都对应100条数据。

 1 INSERT OVERWRITE TABLE test
 2 SELECT CAST(CASE WHEN id < 908000000 THEN (9500000 + (CAST (RAND() * 2 AS INT) + 1) * 48 )
 3 ELSE CAST(id/100 AS INT) END AS STRING),
 4 name
 5 FROM student_external
 6 WHERE id BETWEEN 900000000 AND 1050000000;
 7  
 8 INSERT OVERWRITE TABLE test_new
 9 SELECT CAST(CAST(id/100 AS INT) AS STRING),
10 name
11 FROM student_delta_external
12 WHERE id BETWEEN 950000000 AND 950500000;


 
通过如下代码,读取test表对应的文件夹内的数据并转换为JavaPairRDD存于leftRDD中,同样读取test表对应的数据存于rightRDD中。通过RDD的join算子对leftRDD与rightRDD进行Join,并指定并行度为48。

 1 public class SparkDataSkew{
 2 public static void main(String[] args) {
 3 SparkConf sparkConf = new SparkConf();
 4 sparkConf.setAppName("DemoSparkDataFrameWithSkewedBigTableDirect");
 5 sparkConf.set("spark.default.parallelism", String.valueOf(parallelism));
 6 JavaSparkContext javaSparkContext = new JavaSparkContext(sparkConf);
 7  
 8 JavaPairRDD<String, String> leftRDD = javaSparkContext.textFile("hdfs://hadoop1:8020/apps/hive/warehouse/default/test/")
 9 .mapToPair((String row) -> {
10 String[] str = row.split(",");
11 return new Tuple2<String, String>(str[0], str[1]);
12 });
13  
14 JavaPairRDD<String, String> rightRDD = javaSparkContext.textFile("hdfs://hadoop1:8020/apps/hive/warehouse/default/test_new/")
15 .mapToPair((String row) -> {
16 String[] str = row.split(",");
17 return new Tuple2<String, String>(str[0], str[1]);
18 });
19  
20 leftRDD.join(rightRDD, parallelism)
21 .mapToPair((Tuple2<String, Tuple2<String, String>> tuple) -> new Tuple2<String, String>(tuple._1(), tuple._2()._2()))
22 .foreachPartition((Iterator<Tuple2<String, String>> iterator) -> {
23 AtomicInteger atomicInteger = new AtomicInteger();
24 iterator.forEachRemaining((Tuple2<String, String> tuple) -> atomicInteger.incrementAndGet());
25 });
26  
27 javaSparkContext.stop();
28 javaSparkContext.close();
29 }
30 }

从下图可看出,整个Join耗时1分54秒,其中Join Stage耗时1.7分钟。
few skewed key join

通过分析Join Stage的所有Task可知,在其它Task所处理记录数为192.71万的同时Task 32的处理的记录数为992.72万,故它耗时为1.7分钟,远高于其它Task的约10秒。这与上文准备数据集时,将id为9500048为9500096对应的数据量设置非常大,其它id对应的数据集非常均匀相符合。
few skewed key join

现通过如下操作,实现倾斜Key的分散处理

  • 将leftRDD中倾斜的key(即9500048与9500096)对应的数据单独过滤出来,且加上1到24的随机前缀,并将前缀与原数据用逗号分隔(以方便之后去掉前缀)形成单独的leftSkewRDD
  • 将rightRDD中倾斜key对应的数据抽取出来,并通过flatMap操作将该数据集中每条数据均转换为24条数据(每条分别加上1到24的随机前缀),形成单独的rightSkewRDD
  • 将leftSkewRDD与rightSkewRDD进行Join,并将并行度设置为48,且在Join过程中将随机前缀去掉,得到倾斜数据集的Join结果skewedJoinRDD
  • 将leftRDD中不包含倾斜Key的数据抽取出来作为单独的leftUnSkewRDD
  • 对leftUnSkewRDD与原始的rightRDD进行Join,并行度也设置为48,得到Join结果unskewedJoinRDD
  • 通过union算子将skewedJoinRDD与unskewedJoinRDD进行合并,从而得到完整的Join结果集

具体实现代码如下

 1 public class SparkDataSkew{
 2 public static void main(String[] args) {
 3 int parallelism = 48;
 4 SparkConf sparkConf = new SparkConf();
 5 sparkConf.setAppName("SolveDataSkewWithRandomPrefix");
 6 sparkConf.set("spark.default.parallelism", parallelism + "");
 7 JavaSparkContext javaSparkContext = new JavaSparkContext(sparkConf);
 8  
 9 JavaPairRDD<String, String> leftRDD = javaSparkContext.textFile("hdfs://hadoop1:8020/apps/hive/warehouse/default/test/")
10 .mapToPair((String row) -> {
11 String[] str = row.split(",");
12 return new Tuple2<String, String>(str[0], str[1]);
13 });
14  
15 JavaPairRDD<String, String> rightRDD = javaSparkContext.textFile("hdfs://hadoop1:8020/apps/hive/warehouse/default/test_new/")
16 .mapToPair((String row) -> {
17 String[] str = row.split(",");
18 return new Tuple2<String, String>(str[0], str[1]);
19 });
20  
21 String[] skewedKeyArray = new String[]{"9500048", "9500096"};
22 Set<String> skewedKeySet = new HashSet<String>();
23 List<String> addList = new ArrayList<String>();
24 for(int i = 1; i <=24; i++) {
25 addList.add(i + "");
26 }
27 for(String key : skewedKeyArray) {
28 skewedKeySet.add(key);
29 }
30  
31 Broadcast<Set<String>> skewedKeys = javaSparkContext.broadcast(skewedKeySet);
32 Broadcast<List<String>> addListKeys = javaSparkContext.broadcast(addList);
33  
34 JavaPairRDD<String, String> leftSkewRDD = leftRDD
35 .filter((Tuple2<String, String> tuple) -> skewedKeys.value().contains(tuple._1()))
36 .mapToPair((Tuple2<String, String> tuple) -> new Tuple2<String, String>((new Random().nextInt(24) + 1) + "," + tuple._1(), tuple._2()));
37  
38 JavaPairRDD<String, String> rightSkewRDD = rightRDD.filter((Tuple2<String, String> tuple) -> skewedKeys.value().contains(tuple._1()))
39 .flatMapToPair((Tuple2<String, String> tuple) -> addListKeys.value().stream()
40 .map((String i) -> new Tuple2<String, String>( i + "," + tuple._1(), tuple._2()))
41 .collect(Collectors.toList())
42 .iterator()
43 );
44  
45 JavaPairRDD<String, String> skewedJoinRDD = leftSkewRDD
46 .join(rightSkewRDD, parallelism)
47 .mapToPair((Tuple2<String, Tuple2<String, String>> tuple) -> new Tuple2<String, String>(tuple._1().split(",")[1], tuple._2()._2()));
48  
49 JavaPairRDD<String, String> leftUnSkewRDD = leftRDD.filter((Tuple2<String, String> tuple) -> !skewedKeys.value().contains(tuple._1()));
50 JavaPairRDD<String, String> unskewedJoinRDD = leftUnSkewRDD.join(rightRDD, parallelism).mapToPair((Tuple2<String, Tuple2<String, String>> tuple) -> new Tuple2<String, String>(tuple._1(), tuple._2()._2()));
51  
52 skewedJoinRDD.union(unskewedJoinRDD).foreachPartition((Iterator<Tuple2<String, String>> iterator) -> {
53 AtomicInteger atomicInteger = new AtomicInteger();
54 iterator.forEachRemaining((Tuple2<String, String> tuple) -> atomicInteger.incrementAndGet());
55 });
56  
57 javaSparkContext.stop();
58 javaSparkContext.close();
59 }
60 }

通过分析Join Stage的所有Task可知从下图可看出,整个Join耗时58秒,其中Join Stage耗时33秒。
few skewed key join

  • 由于Join分倾斜数据集Join和非倾斜数据集Join,而各Join的并行度均为48,故总的并行度为96
  • 由于提交任务时,设置的Executor个数为4,每个Executor的core数为12,故可用Core数为48,所以前48个Task同时启动(其Launch时间相同),后48个Task的启动时间各不相同(等待前面的Task结束才开始)
  • 由于倾斜Key被加上随机前缀,原本相同的Key变为不同的Key,被分散到不同的Task处理,故在所有Task中,未发现所处理数据集明显高于其它Task的情况

few skewed key join

实际上,由于倾斜Key与非倾斜Key的操作完全独立,可并行进行。而本实验受限于可用总核数为48,可同时运行的总Task数为48,故而该方案只是将总耗时减少一半(效率提升一倍)。如果资源充足,可并发执行Task数增多,该方案的优势将更为明显。在实际项目中,该方案往往可提升数倍至10倍的效率。

总结

适用场景
两张表都比较大,无法使用Map则Join。其中一个RDD有少数几个Key的数据量过大,另外一个RDD的Key分布较为均匀。

解决方案
将有数据倾斜的RDD中倾斜Key对应的数据集单独抽取出来加上随机前缀,另外一个RDD每条数据分别与随机前缀结合形成新的RDD(相当于将其数据增到到原来的N倍,N即为随机前缀的总个数),然后将二者Join并去掉前缀。然后将不包含倾斜Key的剩余数据进行Join。最后将两次Join的结果集通过union合并,即可得到全部Join结果。

优势
相对于Map则Join,更能适应大数据集的Join。如果资源充足,倾斜部分数据集与非倾斜部分数据集可并行进行,效率提升明显。且只针对倾斜部分的数据做数据扩展,增加的资源消耗有限。

劣势
如果倾斜Key非常多,则另一侧数据膨胀非常大,此方案不适用。而且此时对倾斜Key与非倾斜Key分开处理,需要扫描数据集两遍,增加了开销。

大表随机添加N种随机前缀,小表扩大N倍

原理

如果出现数据倾斜的Key比较多,上一种方法将这些大量的倾斜Key分拆出来,意义不大。此时更适合直接对存在数据倾斜的数据集全部加上随机前缀,然后对另外一个不存在严重数据倾斜的数据集整体与随机前缀集作笛卡尔乘积(即将数据量扩大N倍)。
spark random prefix

案例

这里给出示例代码,读者可参考上文中分拆出少数倾斜Key添加随机前缀的方法,自行测试。

 1 public class SparkDataSkew {
 2 public static void main(String[] args) {
 3 SparkConf sparkConf = new SparkConf();
 4 sparkConf.setAppName("ResolveDataSkewWithNAndRandom");
 5 sparkConf.set("spark.default.parallelism", parallelism + "");
 6 JavaSparkContext javaSparkContext = new JavaSparkContext(sparkConf);
 7  
 8 JavaPairRDD<String, String> leftRDD = javaSparkContext.textFile("hdfs://hadoop1:8020/apps/hive/warehouse/default/test/")
 9 .mapToPair((String row) -> {
10 String[] str = row.split(",");
11 return new Tuple2<String, String>(str[0], str[1]);
12 });
13  
14 JavaPairRDD<String, String> rightRDD = javaSparkContext.textFile("hdfs://hadoop1:8020/apps/hive/warehouse/default/test_new/")
15 .mapToPair((String row) -> {
16 String[] str = row.split(",");
17 return new Tuple2<String, String>(str[0], str[1]);
18 });
19  
20 List<String> addList = new ArrayList<String>();
21 for(int i = 1; i <=48; i++) {
22 addList.add(i + "");
23 }
24  
25 Broadcast<List<String>> addListKeys = javaSparkContext.broadcast(addList);
26  
27 JavaPairRDD<String, String> leftRandomRDD = leftRDD.mapToPair((Tuple2<String, String> tuple) -> new Tuple2<String, String>(new Random().nextInt(48) + "," + tuple._1(), tuple._2()));
28  
29 JavaPairRDD<String, String> rightNewRDD = rightRDD
30 .flatMapToPair((Tuple2<String, String> tuple) -> addListKeys.value().stream()
31 .map((String i) -> new Tuple2<String, String>( i + "," + tuple._1(), tuple._2()))
32 .collect(Collectors.toList())
33 .iterator()
34 );
35  
36 JavaPairRDD<String, String> joinRDD = leftRandomRDD
37 .join(rightNewRDD, parallelism)
38 .mapToPair((Tuple2<String, Tuple2<String, String>> tuple) -> new Tuple2<String, String>(tuple._1().split(",")[1], tuple._2()._2()));
39  
40 joinRDD.foreachPartition((Iterator<Tuple2<String, String>> iterator) -> {
41 AtomicInteger atomicInteger = new AtomicInteger();
42 iterator.forEachRemaining((Tuple2<String, String> tuple) -> atomicInteger.incrementAndGet());
43 });
44  
45 javaSparkContext.stop();
46 javaSparkContext.close();
47 }
48 }

总结 

适用场景
一个数据集存在的倾斜Key比较多,另外一个数据集数据分布比较均匀。

优势
对大部分场景都适用,效果不错。

劣势
需要将一个数据集整体扩大N倍,会增加资源消耗。

总结

对于数据倾斜,并无一个统一的一劳永逸的方法。更多的时候,是结合数据特点(数据集大小,倾斜Key的多少等)综合使用上文所述的多种方法。

转载于:https://www.cnblogs.com/zuizui1204/p/7920692.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/354497.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JavaParser入门:以编程方式分析Java代码

我最喜欢的事情之一是解析代码并对其执行自动操作。 因此&#xff0c;我开始为JavaParser做出贡献&#xff0c;并创建了两个相关项目&#xff1a; java-symbol-solver和Effectivejava 。 作为JavaParser的贡献者&#xff0c;我反复阅读了一些非常类似的问题&#xff0c;这些问…

python Django基本介绍

创建Django项目并运行 实验环境&#xff1a; Ubuntu 16.04下安装好Anaconda3 Windows下安装好PyCharm 实验步骤 一、创建django工程 在Ubuntu 16.04下执行下面的命令。 &#xff08;1&#xff09;创建一个python3的虚拟环境&#xff08;如果已经创建&#xff0c;忽略此步&…

Android 热补丁动态修复框架小结

Android 热补丁动态修复框架小结转载于:https://www.cnblogs.com/zhujiabin/p/7923233.html

C语言中关于结构体的理解

在c语言中我们如果需要去表示一个学生的特征&#xff0c;例如名字年龄成绩&#xff0c;这些信息我们就需要用到结构体来描述了。 struct stu{char name[20]; //姓名int age; //年龄float score; //成绩 }; struct(结构体)&#xff1a;是由一系列具有相同类型…

GoldenGate Logdump基本使用

Logdump是GoldenGate复制软件中附带的一个工具软件&#xff0c;在OGG的目录下可以找到。这个工具主要用于分析OGG生成的队列文件&#xff0c;查找记录、统计队列文件中的数据等。 在OGG安装目录下执行logdump.exe or ./logdump即可进入命令行。 开始查找记录之前&#xff0c;先…

js 里面的键盘事件对应的键码

js 里面的键盘事件经常用到&#xff0c;所以收集了键盘事件对应的键码来分享下&#xff1a; keyCode 8 BackSpace BackSpace keyCode 9 Tab Tab keyCode 12 Clear keyCode 13 Enter keyCode 16 Shift_L keyCode 17 Control_L keyCode 18 Alt_L keyCode 19 Pause keyCo…

.bam.bai的意义_业务活动监视器(BAM)2.0带来的革命

.bam.bai的意义生产兼具精益和企业价值的中间件是一项艰巨的工作。 它要么不存在&#xff0c;要么需要创新的思维&#xff08;很多&#xff09;&#xff0c;并且需要在实现中反复进行。 业务风险很大&#xff0c;但是如果您做对了&#xff0c;它就会使您领先于其他任何公司。 这…

数据结构和算法之排序五:选择排序

我们上一篇谈到了冒泡排序&#xff0c;其实我也说了&#xff0c;这两个排序方式何其相似&#xff0c;如果掌握了冒泡排序再来进行选择排序的理解我觉得完全没有太大的问题。那么什么叫做选择排序呢&#xff1f;我们可以理解为矮子里面挑高个&#xff0c;比如说呀有一个富翁来到…

Visual Studio Code使用问题

1、打开vscode黑屏 右击vscode快捷方式–>属性–>兼容性—>兼容模式打钩 重启vscode就可以了。 2、vscode终端没有显示路径&#xff0c;不能输入 显示如下图 则关闭VS Code ,右键单击VS Code 图标&#xff0c;选择属性->兼容性&#xff0c;取消勾选 已兼容模式运…

Java社区调查结果:74%的开发人员希望减少详细程度

一个新的JDK增强建议&#xff08;JEP&#xff09;在Java社区中风起云涌&#xff1a;JEP286。该建议建议在Java的未来版本中引入局部变量类型推断&#xff0c;以简化Java应用程序的编写。 在下面的文章中&#xff0c;我们将解释它的含义以及它将如何影响您的代码。 新帖&#…

【BZOJ2300】[HAOI2011]防线修建 set维护凸包

【BZOJ2300】[HAOI2011]防线修建 Description 近来A国和B国的矛盾激化&#xff0c;为了预防不测&#xff0c;A国准备修建一条长长的防线&#xff0c;当然修建防线的话&#xff0c;肯定要把需要保护的城市修在防线内部了。可是A国上层现在还犹豫不决&#xff0c;到底该把哪些城市…

Sass笔记(CSS 的预编译语言)

在线转换&#xff1a;https://www.sass.hk/css2sass/ Sass 是一种 CSS 的预编译语言。它提供了 变量&#xff08;variables&#xff09;、嵌套&#xff08;nested rules&#xff09;、 混合&#xff08;mixins&#xff09;、 函数&#xff08;functions&#xff09;等功能。 S…

mysql 主从同步-读写分离

主从同步与读写分离测试 一、 实验环境(主从同步) Master centos 7.3 192.168.138.13 Slave centos 7.3 192.168.138.14 二、在master操作 安装并配置文件[rootlocalhost ~]# yum install mariadb-server …

coherence安装_Oracle Coherence:分布式数据管理

coherence安装本文介绍如何使用Oracle Coherence提供分布式&#xff08;分区&#xff09;数据管理。 在下面的示例应用程序中&#xff0c;创建了一个名为OTV的新集群&#xff0c;并且在该集群的两个成员之间分配了一个名为user-map的缓存对象。 二手技术&#xff1a; JDK 1.6.…

AtomicReference 原子引用

原创 2016年03月04日 13:35:2076381.简介 赋值操作不是线程安全的。若想不用锁来实现&#xff0c;可以用AtomicReference<V>这个类&#xff0c;实现对象引用的原子更新。 使用场景&#xff1a;一个线程使用student对象&#xff0c;另一个线程负责定时读表&#xff0c;更新…

MongoDB基础使用

5.MongoDB基础使用 5.1.常用的命令&#xff08;重点&#xff09; > Help 查看帮助 显示数据库列表 > show dbs 创建数据库 > use dbname 如果数据库不存在&#xff0c;则创建数据库dbname&#xff0c;否则切换到指定数据库dbname。创建的数据库并不在数据库的列表中…

使用一些可选的将字符串配置属性转换为其他类型

有一天&#xff0c;您遇到一些代码&#xff0c;并认为这很漂亮&#xff0c;为什么我没有想到呢&#xff1f; 因此&#xff0c;我的长期同事Mark Warner在使用方法引用处理从String进行转换的标准名称/值存储模式方面有一个不错的选择。 int size store.getProperty("cac…

AttributeError: module 're' has no attribute 'sub'

文件不能同名&#xff08;import re re.py)转载于:https://www.cnblogs.com/realmonkeykingsun/p/7992613.html

django-uwsgi配置

一、安装uwsgi uwsgi是一个应用服务器&#xff0c;非静态文件的网络请求就必须通过他完成&#xff0c;他也可以充当静态文件服务器&#xff0c;但不是它的强项。 注意&#xff1a;uwsgi必须安装在系统级别的Python环境中&#xff0c;不要安装到虚拟环境中。 uwsgi是使用pytho…

C++堆和栈详解(转)

一、预备知识—程序的内存分配 一个由C/C编译的程序占用的内存分为以下几个部分 1、栈区&#xff08;stack&#xff09;— 由编译器自动分配释放 &#xff0c;存放函数的参数值&#xff0c;局部变量的值等。其 操作方式类似于数据结构中的栈。 2、堆区&#xf…