diff --git a/Day01-15/06.函数和模块的使用.md b/Day01-15/06.函数和模块的使用.md index 4495917..4badd51 100644 --- a/Day01-15/06.函数和模块的使用.md +++ b/Day01-15/06.函数和模块的使用.md @@ -344,7 +344,7 @@ if __name__ == '__main__': 在实际开发中,我们应该尽量减少对全局变量的使用,因为全局变量的作用域和影响过于广泛,可能会发生意料之外的修改和使用,除此之外全局变量比局部变量拥有更长的生命周期,可能导致对象占用的内存长时间无法被[垃圾回收](https://zh.wikipedia.org/wiki/%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6_(%E8%A8%88%E7%AE%97%E6%A9%9F%E7%A7%91%E5%AD%B8))。事实上,减少对全局变量的使用,也是降低代码之间耦合度的一个重要举措,同时也是对[迪米特法则](https://zh.wikipedia.org/zh-hans/%E5%BE%97%E5%A2%A8%E5%BF%92%E8%80%B3%E5%AE%9A%E5%BE%8B)的践行。减少全局变量的使用就意味着我们应该尽量让变量的作用域在函数的内部,但是如果我们希望将一个局部变量的生命周期延长,使其在定义它的函数调用结束后依然可以使用它的值,这时候就需要使用[闭包](https://zh.wikipedia.org/wiki/%E9%97%AD%E5%8C%85_(%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6)),这个我们在后续的内容中进行讲解。 -> **说明:** 很多人经常会将“闭包”和[“匿名函数”](https://zh.wikipedia.org/wiki/%E5%8C%BF%E5%90%8D%E5%87%BD%E6%95%B0)混为一谈,但实际上它们是不同的概念,如果想提前了解这个概念,推荐看看[维基百科](https://zh.wikipedia.org/wiki/)或者[知乎](https://www.zhihu.com/)上对这个概念的讨论。 +> **说明:** 很多人经常会将“闭包”和[“匿名函数”](https://zh.wikipedia.org/wiki/%E5%8C%BF%E5%90%8D%E5%87%BD%E6%95%B0)混为一谈,但实际上它们并不是一回事,如果想了解这个概念,可以看看[维基百科](https://zh.wikipedia.org/wiki/)的解释或者[知乎](https://www.zhihu.com/)上对这个概念的讨论。 说了那么多,其实结论很简单,从现在开始我们可以将Python代码按照下面的格式进行书写,这一点点的改进其实就是在我们理解了函数和作用域的基础上跨出的巨大的一步。 diff --git a/Day01-15/07.字符串和常用数据结构.md b/Day01-15/07.字符串和常用数据结构.md index e3b94eb..57127b3 100644 --- a/Day01-15/07.字符串和常用数据结构.md +++ b/Day01-15/07.字符串和常用数据结构.md @@ -276,7 +276,7 @@ if __name__ == '__main__': ### 使用元组 -Python 中的元组与列表类似也是一种容器数据类型,可以用一个变量(对象)来存储多个数据,不同之处在于元组的元素不能修改,在前面的代码中我们已经不止一次使用过元组了。顾名思义,我们把多个元素组合到一起就形成了一个元组,所以它和列表一样可以保存多条数据。下面的代码演示了如何定义和使用元组。 +Python中的元组与列表类似也是一种容器数据类型,可以用一个变量(对象)来存储多个数据,不同之处在于元组的元素不能修改,在前面的代码中我们已经不止一次使用过元组了。顾名思义,我们把多个元素组合到一起就形成了一个元组,所以它和列表一样可以保存多条数据。下面的代码演示了如何定义和使用元组。 ```Python # 定义元组 @@ -319,30 +319,39 @@ Python中的集合跟数学上的集合是一致的,不允许有重复元素 ![](./res/python-set.png) +可以按照下面代码所示的方式来创建和使用集合。 + ```Python +# 创建集合的字面量语法 set1 = {1, 2, 3, 3, 3, 2} print(set1) print('Length =', len(set1)) +# 创建集合的构造器语法(面向对象部分会进行详细讲解) set2 = set(range(1, 10)) -print(set2) +set3 = set((1, 2, 3, 3, 2, 1)) +print(set2, set3) +# 创建集合的推导式语法(推导式也可以用于推导集合) +set4 = {num for num in range(1, 100) if num % 3 == 0 or num % 5 == 0} +print(set4) +``` + +向集合添加元素和从集合删除元素。 + +```Python set1.add(4) set1.add(5) set2.update([11, 12]) -print(set1) -print(set2) set2.discard(5) -# remove的元素如果不存在会引发KeyError if 4 in set2: set2.remove(4) -print(set2) -# 遍历集合容器 -for elem in set2: - print(elem ** 2, end=' ') -print() -# 将元组转换成集合 -set3 = set((1, 2, 3, 3, 2, 1)) +print(set1, set2) print(set3.pop()) print(set3) +``` + +集合的成员、交集、并集、差集等运算。 + +```Python # 集合的交集、并集、差集、对称差运算 print(set1 & set2) # print(set1.intersection(set2)) @@ -367,16 +376,25 @@ print(set1 >= set3) ### 使用字典 -字典是另一种可变容器模型,类似于我们生活中使用的字典,它可以存储任意类型对象,与列表、集合不同的是,字典的每个元素都是由一个键和一个值组成的“键值对”,键和值通过冒号分开。下面的代码演示了如何定义和使用字典。 +字典是另一种可变容器模型,Python中的字典跟我们生活中使用的字典是一样一样的,它可以存储任意类型对象,与列表、集合不同的是,字典的每个元素都是由一个键和一个值组成的“键值对”,键和值通过冒号分开。下面的代码演示了如何定义和使用字典。 ```Python +# 创建字典的字面量语法 scores = {'骆昊': 95, '白元芳': 78, '狄仁杰': 82} +print(scores) +# 创建字典的构造器语法 +items1 = dict(one=1, two=2, three=3, four=4) +# 通过zip函数将两个序列压成字典 +items2 = dict(zip(['a', 'b', 'c'], '123')) +# 创建字典的推导式语法 +items3 = {num: num ** 2 for num in range(1, 10)} +print(items1, items2, items3) # 通过键可以获取字典中对应的值 print(scores['骆昊']) print(scores['狄仁杰']) -# 对字典进行遍历(遍历的其实是键再通过键取对应的值) -for elem in scores: - print('%s\t--->\t%d' % (elem, scores[elem])) +# 对字典中所有键值对进行遍历 +for key in scores: + print(f'{key}: {scores[key]}') # 更新字典中的元素 scores['白元芳'] = 65 scores['诸葛王朗'] = 71 diff --git a/Day91-100/93.MySQL性能优化.md b/Day91-100/93.MySQL性能优化.md index de8beed..65e6e10 100644 --- a/Day91-100/93.MySQL性能优化.md +++ b/Day91-100/93.MySQL性能优化.md @@ -4,41 +4,66 @@ 在前面[《关系型数据库MySQL》](../Day36-40/36-38.关系型数据库MySQL.md)一文中,我们已经讲到过索引的相关知识,这里我们做一个简单的回顾。 -1. B-Tree索引 -2. HASH索引 -3. R-Tree索引(空间索引) -4. Full-text索引(全文索引) +#### 索引的设计原则 + +1. 创建索引的列并不一定是`select`操作中要查询的列,最适合做索引的列是出现在`where`子句中经常用作筛选条件或连表子句中作为表连接条件的列。 +2. 具有唯一性的列,索引效果好;重复值较多的列,索引效果差。 +3. 如果为字符串类型创建索引,最好指定一个前缀长度,创建短索引。短索引可以减少磁盘I/O而且在做比较时性能也更好,更重要的是MySQL底层的高速索引缓存能够缓存更多的键值。 +4. 创建一个包含N列的复合索引(多列索引)时,相当于是创建了N个索引,此时应该利用最左前缀进行匹配。 +5. 不要过度使用索引。索引并不是越多越好,索引需要占用额外的存储空间而且会影响写操作的性能(插入、删除、更新数据时索引也需要更新)。MySQL在生成执行计划时,要考虑各个索引的使用,这个也是需要耗费时间的。 +6. 要注意可能使索引失效的场景,例如:模糊查询使用了前置通配符、使用负向条件进行查询等。 ### 使用过程 -过程,通常也称之为存储过程。 +过程,通常也称之为存储过程,它是事先编译好存储在数据库中的一组SQL的集合。调用存储过程可以简化应用程序开发人员的工作,减少与数据库服务器之间的通信,对于提升数据操作的性能是有帮助的,这些我们在之前的[《关系型数据库MySQL》](../Day36-40/36-38.关系型数据库MySQL.md)一文中已经提到过。 -```SQL -create procedure ... (params) -begin -... -end; -call ... -``` - -```Python -cursor.callproc('...') -``` ### 数据分区 +MySQL支持做数据分区,通过分区可以存储更多的数据、优化查询,获得更大的吞吐量并快速删除过期的数据。关于这个知识点建议大家看看MySQL的[官方文档](https://dev.mysql.com/doc/refman/5.7/en/partitioning-overview.html)。数据分区有以下几种类型: +1. RANGE分区:基于连续区间范围,把数据分配到不同的分区。 + + ```SQL + CREATE TABLE tb_emp ( + eno INT NOT NULL, + ename VARCHAR(20) NOT NULL, + job VARCHAR(10) NOT NULL, + hiredate DATE NOT NULL, + dno INT NOT NULL + ) + PARTITION BY RANGE( YEAR(hiredate) ) ( + PARTITION p0 VALUES LESS THAN (1960), + PARTITION p1 VALUES LESS THAN (1970), + PARTITION p2 VALUES LESS THAN (1980), + PARTITION p3 VALUES LESS THAN (1990), + PARTITION p4 VALUES LESS THAN MAXVALUE + ); + ``` + +2. LIST分区:基于枚举值的范围,把数据分配到不同的分区。 + +3. HASH分区 / KEY分区:基于分区个数,把数据分配到不同的分区。 + + ```SQL + CREATE TABLE tb_emp ( + eno INT NOT NULL, + ename VARCHAR(20) NOT NULL, + job VARCHAR(10) NOT NULL, + hiredate DATE NOT NULL, + dno INT NOT NULL + ) + PARTITION BY HASH(dno) + PARTITIONS 4; + ``` ### SQL优化 1. 通过`show status`了解各种SQL的执行频率。 ```SQL - show status like 'com_%'; - show status like 'innodb_%'; - show status like 'connections'; - show status like 'slow_queries'; + ``` 2. 定位低效率的SQL语句 - 慢查询日志。 @@ -47,26 +72,48 @@ cursor.callproc('...') show processlist ``` -3. 通过`explain`了解SQL的执行计划。 +3. 通过`explain`了解SQL的执行计划。例如: - - select_type:查询类型(simple、primary、union、subquery) - - table:输出结果集的表 - - type:访问类型(ALL、index、range、ref、eq_ref、const、NULL) - - possible_keys:查询时可能用到的索引 - - key:实际使用的索引 - - key_len:索引字段的长度 - - rows:扫描的行数 - - extra:额外信息 + ```SQL + explain select ename, job, sal from tb_emp where dno=20\G + *************************** 1. row *************************** + id: 1 + select_type: SIMPLE + table: tb_emp + type: ref + possible_keys: fk_emp_dno + key: fk_emp_dno + key_len: 5 + ref: const + rows: 7 + Extra: NULL + 1 row in set (0.00 sec) + ``` + + - `select_type`:查询类型(SIMPLE - 简单查询、PRIMARY - 主查询、UNION - 并集、SUBQUERY - 子查询)。 + - `table`:输出结果集的表。 + - `type`:访问类型(ALL - 全表查询性能最差、index、range、ref、eq_ref、const、NULL)。 + - `possible_keys`:查询时可能用到的索引。 + - `key`:实际使用的索引。 + - `key_len`:索引字段的长度。 + - `rows`:扫描的行数,行数越少肯定性能越好。 + - `extra`:额外信息。 4. 通过`show profiles`和`show profile for query`分析SQL。 5. 优化CRUD操作。 - - 优化insert语句 - - 优化order by语句 - - 优化group by语句 + - 优化`insert`语句 + - 在`insert`语句后面跟上多组值进行插入在性能上优于分开`insert`。 + - 如果有多个连接向同一个表插入数据,使用`insert delayed`可以获得更好的性能。 + - 如果要从一个文本文件装载数据到表时,使用`load data infile`比`insert`性能好得多。 + - 优化`order by`语句 + - 如果`where`子句的条件和`order by`子句的条件相同,而且排序的顺序与索引的顺序相同,如果还同时满足排序字段都是升序或者降序,那么只靠索引就能完成排序。 + - 优化`group by`语句 + - 在使用`group by`子句分组时,如果希望避免排序带来的开销,可以用`order by null`禁用排序。 - 优化嵌套查询 - 优化or条件 + - 如果条件之间是`or`关系,则只有在所有条件都用到索引的情况下索引才会生效。 - 优化分页查询 - 使用SQL提示 - USE INDEX @@ -75,24 +122,43 @@ cursor.callproc('...') ### 配置优化 +可以使用下面的命令来查看MySQL服务器配置参数的默认值。 + +```SQL +show variables; +show variables like 'key_%'; +show variables like '%cache%'; +show variables like 'innodb_buffer_pool_size'; +``` + +通过下面的命令可以了解MySQL服务器运行状态值。 + +```SQL +show status; +show status like 'com_%'; +show status like 'innodb_%'; +show status like 'connections'; +show status like 'slow_queries'; +``` + 1. 调整max_connections 2. 调整back_log 3. 调整table_open_cache 4. 调整thread_cache_size 5. 调整innodb_lock_wait_timeout +6. 调整`innodb_buffer_pool_size`:InnoDB数据和索引的内存缓冲区大小,以字节为单位,这个值设置得越高,访问表数据需要进行的磁盘I/O操作就越少,如果可能甚至可以将该值设置为物理内存大小的80%。 ### 架构优化 -1. 通过拆分提高表的访问效率 +1. 通过拆分提高表的访问效率。 - 垂直拆分 - 水平拆分 -2. 逆范式理论 - - 数据表设计的规范程度称之为范式(Normal Form) - - 1NF:列不能再拆分 - - 2NF:所有的属性都依赖于主键 - - 3NF:所有的属性都直接依赖于主键(消除传递依赖) - - BCNF:消除非平凡多值依赖 -3. 使用中间表提高统计查询速度 -4. 主从复制和读写分离 -5. 配置MySQL集群 +2. 逆范式理论。数据表设计的规范程度称之为范式(Normal Form),要提升表的规范程度通常需要将大表拆分为更小的表,范式级别越高数据冗余越小,而且在插入、删除、更新数据时出问题的可能性会大幅度降低,但是节省了空间就意味着查询数据时可能花费更多的时间,原来的单表查询可能会变成连表查询。为此,项目实践中我们通常会进行逆范式操作,故意降低范式级别增加冗余来减少查询的时间开销。 + - 1NF:列不能再拆分 + - 2NF:所有的属性都依赖于主键 + - 3NF:所有的属性都直接依赖于主键(消除传递依赖) + - BCNF:消除非平凡多值依赖 +3. 使用中间表提高统计查询速度。 +4. 主从复制和读写分离,具体内容请参考[《项目部署上线和性能调优》](./98.项目部署上线和性能调优.md)。 +5. 配置MySQL集群。 diff --git a/番外篇/code/Test01.java b/番外篇/code/Test01.java new file mode 100644 index 0000000..a8862a7 --- /dev/null +++ b/番外篇/code/Test01.java @@ -0,0 +1,6 @@ +class Test01 { + + public static void main(String[] args) { + System.out.println("hello, world!"); + } +} \ No newline at end of file diff --git a/番外篇/code/Test02.java b/番外篇/code/Test02.java new file mode 100644 index 0000000..3c01d71 --- /dev/null +++ b/番外篇/code/Test02.java @@ -0,0 +1,10 @@ +class Test02 { + + public static void main(String[] args) { + int total = 0; + for (int i = 1; i <= 100; ++i) { + total += i; + } + System.out.println(total); + } +} \ No newline at end of file diff --git a/番外篇/code/Test03.java b/番外篇/code/Test03.java new file mode 100644 index 0000000..5f6c62a --- /dev/null +++ b/番外篇/code/Test03.java @@ -0,0 +1,33 @@ +import java.util.List; +import java.util.ArrayList; +import java.util.Collections; + +class Test03 { + + /** + * 产生[min, max)范围的随机整数 + */ + public static int randomInt(int min, int max) { + return (int) (Math.random() * (max - min) + min); + } + + public static void main(String[] args) { + List redBalls = new ArrayList<>(); + for (int i = 1; i <= 33; ++i) { + redBalls.add(i); + } + List selectedBalls = new ArrayList<>(); + for (int i = 0; i < 6; ++i) { + selectedBalls.add(redBalls.remove(randomInt(0, redBalls.size()))); + } + Collections.sort(selectedBalls); + selectedBalls.add(randomInt(1, 17)); + for (int i = 0; i < selectedBalls.size(); ++i) { + System.out.printf("%02d ", selectedBalls.get(i)); + if (i == selectedBalls.size() - 2) { + System.out.print("| "); + } + } + System.out.println(); + } +} \ No newline at end of file diff --git a/番外篇/code/test01.py b/番外篇/code/test01.py new file mode 100644 index 0000000..ede9bab --- /dev/null +++ b/番外篇/code/test01.py @@ -0,0 +1 @@ +print('hello, world!') \ No newline at end of file diff --git a/番外篇/code/test02.py b/番外篇/code/test02.py new file mode 100644 index 0000000..344df11 --- /dev/null +++ b/番外篇/code/test02.py @@ -0,0 +1 @@ +print(sum(range(1, 101))) \ No newline at end of file diff --git a/番外篇/code/test03.py b/番外篇/code/test03.py new file mode 100644 index 0000000..d04420f --- /dev/null +++ b/番外篇/code/test03.py @@ -0,0 +1,16 @@ +from random import randint, sample + +# 初始化备选红色球 +red_balls = [x for x in range(1, 34)] +# 选出六个红色球 +selected_balls = sample(red_balls, 6) +# 对红色球进行排序 +selected_balls.sort() +# 添加一个蓝色球 +selected_balls.append(randint(1, 16)) +# 输出选中的随机号码 +for index, ball in enumerate(selected_balls): + print('%02d' % ball, end=' ') + if index == len(selected_balls) - 2: + print('|', end=' ') +print() \ No newline at end of file diff --git a/番外篇/为什么我选择了Python.md b/番外篇/为什么我选择了Python.md new file mode 100644 index 0000000..d09ca7a --- /dev/null +++ b/番外篇/为什么我选择了Python.md @@ -0,0 +1,117 @@ +## 为什么我选择了Python + +目前,Python语言的发展势头在国内国外都是不可阻挡的,Python凭借其简单优雅的语法,强大的生态圈从众多语言中脱颖而出,如今已经是稳坐编程语言排行榜前三的位置。国内很多Python开发者都是从Java开发者跨界过来的,我自己也不例外。我简单的跟大家交代一下,我为什么选择了Python。 + +### Python vs. Java + +我们通过几个例子来比较一下,做同样的事情Java和Python的代码都是怎么写的。 + +例子1:在终端中输出“hello, world”。 + +Java代码: + +```Java +class Test { + + public static void main(String[] args) { + System.out.println("hello, world"); + } +} +``` + +Python代码: + +```Python +print('hello, world') +``` + +例子2:从1到100求和。 + +Java代码: + +```Java +class Test { + + public static void main(String[] args) { + int total = 0; + for (int i = 1; i <= 100; i += 1) { + total += i; + } + System.out.println(total); + } +} +``` + +Python代码: + +```Python +print(sum(range(1, 101))) +``` + +例子3:双色球随机选号。 + +Java代码: + +```Java +import java.util.List; +import java.util.ArrayList; +import java.util.Collections; + +class Test { + + /** + * 产生[min, max)范围的随机整数 + */ + public static int randomInt(int min, int max) { + return (int) (Math.random() * (max - min) + min); + } + + public static void main(String[] args) { + // 初始化备选红色球 + List redBalls = new ArrayList<>(); + for (int i = 1; i <= 33; ++i) { + redBalls.add(i); + } + List selectedBalls = new ArrayList<>(); + // 选出六个红色球 + for (int i = 0; i < 6; ++i) { + selectedBalls.add(redBalls.remove(randomInt(0, redBalls.size()))); + } + // 对红色球进行排序 + Collections.sort(selectedBalls); + // 添加一个蓝色球 + selectedBalls.add(randomInt(1, 17)); + // 输出选中的随机号码 + for (int i = 0; i < selectedBalls.size(); ++i) { + System.out.printf("%02d ", selectedBalls.get(i)); + if (i == selectedBalls.size() - 2) { + System.out.print("| "); + } + } + System.out.println(); + } +} +``` + +Python代码: + +```Python +from random import randint, sample + +# 初始化备选红色球 +red_balls = [x for x in range(1, 34)] +# 选出六个红色球 +selected_balls = sample(red_balls, 6) +# 对红色球进行排序 +selected_balls.sort() +# 添加一个蓝色球 +selected_balls.append(randint(1, 16)) +# 输出选中的随机号码 +for index, ball in enumerate(selected_balls): + print('%02d' % ball, end=' ') + if index == len(selected_balls) - 2: + print('|', end=' ') +print() +``` + +相信,看完这些例子后,你一定感受到了我选择了Python是有道理的。 \ No newline at end of file