码迷,mamicode.com
首页 > 数据库 > 详细

python数据库进阶

时间:2018-09-26 23:57:52      阅读:383      评论:0      收藏:0      [点我收藏+]

标签:不用   了解   ide   nts   系统   最大的   不一致   生产者消费者   倒序   

第1节 MySQL基础

 

一,说明

1,认识MySQL与创建用户
MySQL是最流行的关系型数据库管理系统之一,由瑞典MySQL AB公司开发,目前属于Oracle公司。MySQL是一种关联数据管理系统,关联数据库将数据保存在不同的表中,而不是将所有数据放在一个大仓库内,这样就增加了速度并提高了灵活性。
?
- 关系型数据库:采用关系模型来组织数据的数据库
- 关系:一张二维表,每个关系都有一个关系名,就是表名
- 模型:行和列(二维),具体指字段跟字段信息
2,关系型数据库和非关系型数据库的区别
  • 关系型数据库通过外键关联来建立表与表之间的关系,

  • 非关系型数据库通常指数据以对象的形式存储在数据库中,而对象之间的关系通过每个对象自身的属性来决定


      比如 有一个学生的数据:
?
            姓名:张三,性别:男,学号:12345,班级:二年级一班
?
    还有一个班级的数据:
?
          班级:二年级一班,班主任:李四
?
# 关系型数据库中,我们创建学生表和班级表来存这两条数据,并且学生表中的班级存储的是班级表中的主键。
?
# 非关系型数据库中,我们创建两个对象,一个是学生对象,一个是班级对象,
3,常见数据库
数据库类型特性优点缺点
关系型数据库SQLite、Oracle、mysql 1、关系型数据库,是指采用了关系模型来组织数据的数据库;2、关系型数据库的最大特点就是事务的一致性;3、简单来说,关系模型指的就是二维表格模型,而一个关系型数据库就是由二维表及其之间的联系所组成的一个数据组织。 1、容易理解:二维表结构是非常贴近逻辑世界一个概念,关系模型相对网状、层次等其他模型来说更容易理解;2、使用方便:通用的SQL语言使得操作关系型数据库非常方便;3、易于维护:丰富的完整性(实体完整性、参照完整性和用户定义的完整性)大大减低了数据冗余和数据不一致的概率;4、支持SQL,可用于复杂的查询。 1、为了维护一致性所付出的巨大代价就是其读写性能比较差;2、固定的表结构;3、高并发读写需求;4、海量数据的高效率读写;
非关系型数据库MongoDb、redis、HBase 1、使用键值对存储数据;2、分布式;3、一般不支持ACID特性;4、非关系型数据库严格上不是一种数据库,应该是一种数据结构化存储方法的集合。 1、无需经过sql层的解析,读写性能很高;2、基于键值对,数据没有耦合性,容易扩展;3、存储数据的格式:nosql的存储格式是key,value形式、文档形式、图片形式等等,文档形式、图片形式等等,而关系型数据库则只支持基础类型。 1、不提供sql支持,学习和使用成本较高;2、无事务处理,附加功能bi和报表等支持也不好;

注1:数据库事务必须具备ACID特性,ACID是Atomic原子性,Consistency一致性,Isolation隔离性,Durability持久性。

注2:数据的持久存储,尤其是海量数据的持久存储,还是需要一种关系数据库。


 

二,登录MySQL

不区分大小写,但是系统默认大写为系统代码,小写为程序员代码

1,通过root用户进入数据库

mysql -uroot -ppassword    # 登录数据库
exit     # 退出
\q       # 退出
2,创建用户名及密码

# 创建用户三个步骤
create user ‘lucky‘@‘%‘ identified by ‘password‘; # 创建用户名(%表示远程连接,可用此用户名登陆)
grant all on *.* to ‘lucky‘@‘%‘;    # 赋予所有权限
flush privileges;                   # 使权限生效
?
# 此时以root用户登陆,登录lucky用户:
\q                                  # 退出root
mysql -ulucky -ppassword;           # 登录lucky用户
select user();                      # 查看当前用户
select database();                  # 查看当前位于哪个数据仓库
3,创建数据库

show databases;       # 查看当前有哪些数据库
create database mydb; # 创建数据库(数据库已存在会报错,如果没有,则创建)
create database if not exists mydb;# 数据库已存在不会报错,如果没有会创建
?
# 对于系统内部的,默认的数据仓库,不需要也不能去修改
drop database mydb;   # 删除数据库
show databases;
create database if not exists mydb;
use mydb;             # 进入数据库
select database();
4,创建数据表

create table student(        # student为创建表的name
  id int,                 # id 为字段编号,int为其数据类型
  name varchar(20),       # name为字段名,varchar为类型,20为长度
  );                       # 建表,分号结尾
show tables;                 # 查看当前数据仓库内有哪些表格
5,查看数据表结构

desc student;               # 查看student数据表的结构
show create table student;  # 查看创建表时有哪些命令
(drop table student;        # 删除表格)
6,常用方法

# 增
?
# 方法一
insert into student (id,name) value (1,‘张三‘); # 向数据表内增加内容into 可以省略
insert into student value(2,‘李四‘);    # ()中的字段名可写可不写,默认按顺序添加
insert into student value(3)          # 也可只插入id
insert into student values(3,‘王五‘),(4,‘赵六‘)
?
select * from student;   # 查询当前数据表
?
# 方法二
insert student set id=5,name=‘钱七‘;
?
-------------------------------------------------------------
?
# 查
?
select * from student;                # 查看student
select * from student where id<=4;    # 查看student 并且id小于4的内容
select name from student where id<=4; # 查看student 内的name 段,并且id小于4的内容
?
-------------------------------------------------------------
?
# 改
?
update student set name=‘name1‘ where id=5;# 将id=5的那一行的name 改为‘name1‘
                                          # 如果不加where 限制条件,则表中的name全被更改
                                         
------------------------------------------------------------                                      
# 删
?
delete from student where name=‘钱七‘;     # 删除所有name=钱七的数据
7,mysql数据类型
数据类型代表内容事例
int 整型 id int
varchar(20) 指定长度,最多65535个字符。变长(超出会自动截掉) name varchar(20)(插入20个字符)
char(4) 指定长度,最多255个字符。定长 sex char(4)(不管插入几个字符都会占4个)
double(4,2) 双精度浮点型,m总个数,d小数位 price double(4,2)
text 可变长度,最多65535个字符 detail text
datetime 日期时间类型YYYY-MM-DD HH:MM:SS dates datetime
enum(‘good‘,‘not good‘) 枚举,在给出的value中选择 ping enum(‘good‘,‘not good‘)
  • 流程


creat table tb2(
id INT,
name VARCHAR(20),
sex CHAR(4),
price DOUBLE(4,2),
detail TEXT,
datae DATATIME,
ping ENUM(‘好评‘,‘差评‘)
);
insert intotb2 value(1,‘裤子‘,‘男‘,20.0,‘这条裤子超级好!‘,now(),‘好评‘);

 

三、小结

1,常用方法
功能用法 
insert [into] student (id,name) value (1,‘王一凡‘); 增的第一个语法
  insert into tanzhou value(2,‘name‘); ()中的字段名可写可不写,默认按顺序添加
  insert into student value(3) 也可只插入id
  insert into tanzhou set id=4,name=‘name1‘; 增的第二个语法
select * from student; 查看student
  select * from student where id<4; 查看student 并且id小于4的内容
  select name from studen where id<4; 查看student 内的name 段,并且id小于4的内容
update student set name=‘name1‘ where id=5; 将id=5的那一行的name 改为‘name1‘
  update tanzhou set name=‘name1‘ 如果不加where 限制条件,则表中的name全被更改
delete from student where name=‘钱七‘; 删除所有name=钱七的数据
2,常用命令
功能用法注释
登录数据库 mysql -uroot -ppassword  
退出 exit 或 \q  
创建用户 create user ‘lucky‘@‘%‘ identified by ‘password‘; %表示远程连接,可用此用户名登陆
赋予所有权限 grant all on *.* to ‘lucky‘@‘%‘;  
使权限生效 flush privileges; flush写入
查看当前用户 select user();  
查看当前位于哪个数据库 select database();  
查看当前有哪些数据库 show databases;  
创建数据库 create database mydb;create database [if not exists] mydb; 创建以mydb为名建立一个数据库,[]为可选参数
删除数据库 drop database mydb;  
进入数据库 use mydb;  
创建数据表 create table [if not exists] student(<br />id int ,<br />name varchar(10)<br />); student为创建表的name <br /> id 表示(元组)字段编号,数据为int型 <br /> name为字段名,varchar为类型,20为长度
查看当前数据仓库内有哪些表 show tables;  
查看表的结构 desc student;  
查看创建表时的命令 show creat table tanzhou;  
删除表格 DROP TABLE tablename;  

第2节 表约束,表关系

 


 

一、表约束:

1.非空约束 ((not null))

例子:
create table tb1(
id int not null,     #非空约束字段,insert 的时候,必须添加字段,不能省略,空字符不等于null
name varchar(20) not null  
);

# 如果在创建表时没有添加非空约束
# 后续手动添加非空约束(必须这个字段,没有NULL值)
alter table tb1      # 修改表结构
modify id int not null;
?
# 取消非空约束
alter table tb1
modify id int;

 

2.唯一约束 (unique key)
  • 确保字段中的值的唯一


例子:
create table tb2(
id int unique key,  # 防止id相同
)

# 如果在创建表时没有添加非空约束
# 后续手动添加唯一约束
alter table tb1      # 修改表结构
add unique key(name);
?
# 删除唯一约束
alter table tb1
drop key name;

 

3.自增长 (auto_increment)
  • auto_increment:自动编号,一般与主键组合使用。一个表里只有一个自增默认情况下,起始值为1,每次的增量为1。


create table stu3(
id int primary key auto_increment, # 自增长,输错时在终端不会占用数据,在其他连接工具如pycharm中就会占用一条数据
name varchar(10)
)auto_increment 100
?
insert into stu3 value(‘a‘);
insert into stu3(name) value(‘bc‘);
select * from stu3;

# 如果在创建表时没有添加自动增长
# 后续手动添加自动增长
alter table stu3      # 修改表结构
modify id int auto_increment;
?
# 删除自动增长
alter table stu3
modify id int;

 

4.默认约束 (default)
  • 初始值设置,插入记录时,如果没有明确为字段赋值,则自动赋予默认值


create table stu4(
id int primary key auto_increment,
name varchar(20) not null,
age int not null default 18      # 默认约束
);
?
insert into stu4 value(1,‘张三‘,‘18‘);
insert into stu4 value(2,‘李四‘,‘‘);        #会报错
insert into stu4(id,name) value(2,‘玲玲‘);  #设置要输入的字段值,age可为空字符串
insert into stu4 value(6,‘‘,19);  #空字符串 != null

# 如果在创建表时没有添加默认约束
# 后续手动添加默认约束
alter table stu4      # 修改表结构
modify age int default 20;
?
# 删除默认约束
alter table stu4
modify age int;

 

5.主键约束 (primary key)
  • 主键作用:可以唯一标识一条数据,每张表里只能有一个主键

  • 主键特性:非空且唯一,表没有主键时,第一个出现的非空且唯一


create table stu2(
id int primary key,  # id是主键,主键不能为空
name varchar(10)
);
?
desc stu2            # 此时可以看到id的key变为pri,且Null下变为NO

# 如果在创建表时没有添加主键约束
# 后续手动添加主键约束
alter table stu2      # 修改表结构
add primary key(id);
?
# 删除主键约束
alter table stu2      # 修改表结构
drop primary key;

 

6.外键约束 (foreign key)
  • 保持数据的一致性、完整性,实现一对一或一对多关系。

  • 外键必须关联到键上面去,一般情况是,关联到另一张表的主键。

  • 因为一个表只在一类信息。用外键来做参照,保证数据的一致性,可以减少数据冗余。


# 表a
create table a(
a_id int primary key auto_increment,
a_name varchar(20) not null
);
?
# 表b
create table b(
b_id int primary ke,
b_name varchar(20)not null,
fy_id int not null,
constraint AB_id foreign key(fy_id) references a(a_id)  # 外键约束定义
);
?
# 说明:
1,通过(fy_id)关联到(a_id)中,然后重新命名为 AB_id(其中constraint 可不写)
2,外键约束定义,B表中的fy_id,只能添加a_id中已有的数据。A表中的a_id 被参照的数据,不能被修改和删除。

# 如果在创建表时没有添加外键约束
# 后续手动添加外键约束
alter table b      # 修改表结构
constraint AB_id foreign key(fy_id) references a(a_id);
?
# 删除外键约束
alter table stu2      # 修改表结构
drop foreign key AB_id;

 

二、表关系

  • 单位表,学生表,课程表,三张表,多对多

  • 一对一为主键和主键关联,一对多是主键和外键关联,加中间表就是多对多

1,一对一:
  • 用外键的方式,把两个主键关联

  • 举例,学生表中有学号、姓名、学院,但学生还有写比如电话,家庭地址等信息,这些信息不会放在学生表中,会新建一个学生的详细信息表来储存。

  • 这时的学生表和学生的详细信息两者的关系就是一对一的关系,因为一个学生只有一条详细信息,**用主键加主键的方式**来实现这种关系


# 建立学生表
create table student(
id int primary key,
name varchar(19) not null
);
insert into student value(1);
?
--------------------------------------------------------------------------------
?
#建立详细学生表
create table student_details(
id_x int primary key,
sex varchar(20) not null,
age int,
address varchar(20) comment‘家庭住址‘,
parents varchar(20),
home_num varchar(20),
foreign key(id_x) references student(id)
);
?
insert into student_details values(1,‘赵六‘)  
?
# 说明:
1,此时student中的id1,对应student_details中的id1。。student中没有插入的name,对应student_details中插入的赵六
2,学生表和学生详情表通过student.id 和student_deatails.id_x关联,形成一对一关系,只能插入主键相同的数据

 

2,一对多:
  • 通常情况下,学校中一个学院可以有很多学生,而一个学生只属于某一个学院

  • 学院与学生之间的关系就是一对多的关系,通过**外键关联**来实现这种关系


# 创建学院表
create table department(
id int primary key,
subject varchar(10) not null
);
?
insert into department values(001,‘Python‘),(002,‘JAVA‘);
?
?
#创建学生表                                
create table vip(
id_v int primary key,          # 学生的id
name_v varchar(10) not null,   # 学生名字
id_w int not null              # 所属学院id,只能添加已有的学院id
constraint SD_id foreign key(id_w) references department(id)  # 外键
);
?
insert into vip value(001,‘王五‘,1);  # (001(id_v), 名字,1(department表的 ID))

 

3,多对多:
  • 举例,学生要报名选修课,一个学生可以报名多门课程,一门课程有很多学生报名,那么学生表和课程表两者就形成了多对多关系

  • 对于多对多关系,需要创建中间表实现


#创建学生表
create table sst(
id int primary key auto_increment,
name varchar(10) not null
);
?
?
#创建选课表(中间表)
create table course(
s_id int primary key,         #记录学生的id
course_id int,                #用来记录课程id
primary key(s_id,course_id)   #联合主键(此组合为唯一的,防止一个id重复报一个课程)
foreign key(s_id) references student(id_v),  #关联学生id 外键
foreign key(course_id) references(course_id) #关联课程id 外键
name varchar(10) not null,
);
?
insert into cours values(001,‘py‘),(002,‘java‘),(003,‘japan‘);
insert into ssh values(11,‘a‘),(22,‘b‘),(33,‘c‘);
insert into course values(22,3),(11,2);    # 用中间表来多对多
select * from course;

 

4,作业

(1)建立选课系统中的5张表:(学院表,学生表,学生详情表,课程表,选课表),并每张表插入4条数据。

(2)用文字描述:这5张表之间,对应关系。并说明如何实现这个对应关系。

 

第3节 MySql 基础三

 

 

补充 alter 命令,修改表结构命令

 


create table tb1(
id int,
name char(4));
#修改表结构,增加字段 年龄
alter table tb1 add age int first; #增加年级字段到第一列;
alter table tb1 drop age ; #删除年龄字段;
alter table tb1 add age int after id;#在id后面增加一个字段age,age在第个字段;
?
#修改已经存在字段的类型
alter table tb1 modify age char(4)  #把age字段修改成char类型;
?
#修改列名(字段名)
alter table tb1 change age sex char(4); #将age字段修改为sex;
?
#修改表名
alter table tb1 rename tb2 #将表tb1修改为tb2
?

第4节 MySQL 单表查询 子查询 关联查询

 

一、MySQL单表查询

mysql> show tables;
+----------------+
| Tables_in_mydb |
+----------------+
| choose         |
| course         |
| details       |
| student       |
| tanzhou       |
+----------------+
mysql> desc tanzhou;
+---------+-------------+------+-----+---------+----------------+
| Field   | Type       | Null | Key | Default | Extra         |
+---------+-------------+------+-----+---------+----------------+
| tz_id   | int(11)     | NO   | PRI | NULL   | auto_increment |
| tz_name | varchar(10) | NO   |     | NULL   |               |
+---------+-------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)
mysql> select * from tanzhou;
+-------+--------------+
| tz_id | tz_name     |
+-------+--------------+
|     1 | 软件学院     |
|     2 | 语言学院     |
|     3 | 市场营销     |
|     4 | 养殖学院     |
+-------+--------------+
4 rows in set (0.00 sec)
mysql> desc student;
+---------+-------------+------+-----+---------+----------------+
| Field   | Type       | Null | Key | Default | Extra         |
+---------+-------------+------+-----+---------+----------------+
| st_id   | int(11)     | NO   | PRI | NULL   | auto_increment |
| st_name | varchar(10) | NO   |     | NULL   |               |
| ts_id   | int(11)     | NO   | MUL | NULL   |               |
+---------+-------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)
mysql> select * from student;
+-------+-----------+-------+
| st_id | st_name   | ts_id |
+-------+-----------+-------+
|     1 | 三花     |     1 |
|     2 | 贾梅     |     1 |
|     3 | 余玲玲   |     3 |
|     4 | 王涛     |     2 |
+-------+-----------+-------+
4 rows in set (0.00 sec)
mysql> desc course;
+--------+-------------+------+-----+---------+----------------+
| Field | Type       | Null | Key | Default | Extra         |
+--------+-------------+------+-----+---------+----------------+
| c_id   | int(11)     | NO   | PRI | NULL   | auto_increment |
| c_name | varchar(10) | YES |     | NULL   |               |
| tzc_id | int(11)     | NO   | MUL | NULL   |               |
+--------+-------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)
mysql> desc choose;
+-------+---------+------+-----+---------+-------+
| Field | Type   | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| sc_id | int(11) | NO   | PRI | NULL   |       |
| cc_id | int(11) | NO   | PRI | NULL   |       |
+-------+---------+------+-----+---------+-------+
2 rows in set (0.01 sec)
?
1、查询所有记录
select * from student;#查询student表中的所有内容;
2、查询选中列记录
select s_name from student;#查询表中所在字段的内容;
3、查询指定条件下的记录
select s_name from student where s_id>4;#查询学号s_id 大于4的学生的姓名;
4、查询后为列取别名
select s_name as 姓名 from student; #将s_name 字段查询并命名为姓名(as可加可不加)
select s_name 姓名 from student; #可以省略 as 在做联合查询时要加上别名,方便区分不同的表
5、模糊查询
select * from student where s_name like ‘三%‘;#查询姓名中带有 ‘三‘的信息
select * from student where s_name like ‘%三‘;#查不出
select * from student where s_name like ‘三_‘;
select * from student where s_name like ‘三__‘#两条下划线表示两个字符
?
6、排序:order by (:ASC 升序(默认升序))
select * from student order by ts_id asc; #升序,默认
select * from student order by ts_id desc;#降序
?
7、限制显示数据数量 LIMIT
select * from student limit 2;#显示前两条数据
select * from student limit 4,3;#从第4条数据开始,显示3条数据
?
8、常用的聚合函数 max,min,sum,avg(平均数),round(avg(age))(四舍五入),count(统计) 通常和其它查询配合使用
mysql> select * from details;
+----+------+------+
| id | age | sex |
+----+------+------+
|  1 |   28 | 男   |
|  2 |   18 | 女   |
|  3 |   28 | 女   |
|  4 |   18 | 男   |
+----+------+------+
?
select max(age) from details;#查询age字段数值最大的数据
?
9、分组查询 GROUP BY   一般和聚合函数一起使用
  HAVEING 分组条件
  HAVEING 后面的字段必须是select 后出来过的字段
   
   #查询每个学院有多少学生
  select count(ts_id) from student group by ts_id; #(count了4次学院出现的次数(一个学院几个学生))
  select count(ts_id) 学生人数 from student group by ts_id;
  select ts_id ‘学院‘,count(ts_id) 学生人数 from student group by ts_id;#每个学院有多少学生
 
  #查询只有一个学生的学院名称
  select ts_id ‘学院‘,count(ts_id) 学生人数 from student group by ts_id having count(ts_id)=1;
 
二、MySQL 子查询 嵌套查询 出现在其它SQL语句内的 select 字句,select 中嵌套select

 


#子查询(要求:嵌套在查询内部,必须始终出现在圆括号内)
#求出学生的平均年龄
select avg(age) from details;
#查找出大于平均年龄的数据
select * from details where age>20;#此时20为上一条查询语句的结果
#将平均数的SQL语句作为子查询放入上一条语句中
select * from details where age>(select avg(age)) from details); #子查询(把第一条查询语句的结果当成条件)
?
#例:查询谭州学院中语言学院和软件学院两个学院的学生信息
select tz_id from tanzhou where tz_name=‘软件‘ or tz_name=‘语言‘;#查询软件学院和语言学院的ID
select * from student where ts_id in (select tz_id from tanzhou where tz_name=‘语言‘ or tz_name=‘软件‘);#查询谭州学院中语言学院和软件学院两个学院的所有学生
三、关联查询(内连接,外连接) 多表查询

1、内连接:(通过外键把把两张表连接起来)

  • 无条件内连接:又名交叉连接/笛卡尔连接。第一张表的每一项会和另一张表的每一项依此组合。

  • 有条件内连接:在无条件的内连接基础上加一个OM子句。当连接的时候,筛选出那些有实际意义的记录行来进行拼接。


#无条件内连接
没有选择的其他学院信息也会显示,有用数据少
数据冗余大,笛卡尔连接(这两种效果一样)
select * from student,tanzhou;
select * from student inner join tanzhou;
?
#有条件内连接 on 条件
只会显示有用信息
select * from student inner join tanzhou on ts_id=tz_id;#(学生id=课程id)

2、外连接:外连接必须要加条件

  • 左外连接:以左边的表为基准。两张表做连接的时候,在连接条件不匹配的时候留下左表中的数据,而右表中的数据以NULL填充。

  • 右外连接:以右边的表为基准。两张表做连接的时候,在连接条件不匹配的时候留下右表中的数据,而左表中的数据以NULL填充。


insert into tanzhou value(5,‘设计‘);
select * from tanzhou left join student on ts_id=tz_id;#使用左连接把学生信息都取出来,该学生没有学院信息的用NULL填充
select * from tanzhou right join student on ts_id=tz_id;#使用右外连接,把没有学院的学生数据也显示出来(设计没有了)
select * from student right join tanzhou on ts_id=tz_id where s_id is null;#此时把没有人的学院(设计)找出来了
?
?
?
#查询学生的学号、姓名、课程、所属学院
select * from student left join tanzhou on tz_id=ts_id#谭州id谭州student id
left join course on tz_id=tzc_id #谭州id和谭州课程id
left join choose on cc_id=c_id;#选课表和课程表

# 针对想要的列的信息查询
select s_id,s_name,c_name,tz_name from student left join tanzhouon on tz_id=ts_id
left join course on tz_id=tzc_id
left join choose on cc_id=c_id;

#找出未选课的同学
select s_id,s_name,c_name,tz_name from student left join tanzhouon on tz_id=ts_id
left join course on tz_id=tzc_id
left join choose on cc_id=c_id where c_name is null
#是NULL时用is
where c_name=‘java‘;
where c_name is null;
作业:

? 1、查出学生神情表性别为男,并同时年龄大于18的;

? 2、根据上述的结果,查出学生表对应的姓名、年龄、家庭住址;

? 3、查出学生的(姓名、年龄、性别、所属学院)

 

 

 

 

 

 

redis 基本操作

1. 什么是Redis


Redis是由意大利人Salvatore Sanfilippo(网名:antirez)开发的一款内存高速缓存数据库。Redis全称为:Remote Dictionary Server,该软件使用C语言编写,Redis是一个key-value存储系统,它支持丰富的数据类型,如:string、list、set、zset(sorted set)、hash。
?
Redis特点:
Redis以内存作为数据存储介质,所以读写数据的效率极高,远远超过数据库。
?
Redis应用场景:
因为Redis交换数据快,所以在服务器中常用来存储一些需要频繁调取的数据,这样可以大大节省系统直接读取磁盘来获得数据的I/O开销,更重要的是可以极大提升速度。
将这种热点数据存到Redis(内存)中,要用的时候,直接从内存取,极大的提高了速度和节约了服务器的开销。

 

进入redis

redis-cli   --raw



#不能进入,就安装:
sudo apt-get update
sudo apt-get install redis-server

 

redis五种数据类型、及操作

  • string 字符串

  • list 列表

  • hash 哈希

  • set 集合

  • sorted sets 有序集合

string


#设置
set key value     例:   ( set name ‘lucky‘ )
# 获取
get key           例:   ( get name   )
# key是唯一的,不能用同一个key 不然就会覆盖

查看过期时间


# -1 表示永久     -2 表示 不存在
ttl key 例:   ( ttl name )

设置过期时间


# 给已经存在 key,设置过期时间
expire key seconds   例: ( expire name 20 )
?
# 设置 key的同时,设置过期时间
set key value ex seconds   例: ( set age 18 ex 20 )

setex key seconds value   例: ( setex sex 20 ‘男‘ )

追加


# 给已有的value,再添加新的值
# append key value

append name love
#get name

 

设置 / 获取 多个


#设置多个 string
mset key value key value..
例子
mset username ‘lucky‘ password  ‘123‘
?
#获取多个
mget key key key ...

mget username password name

key 操作


# 查看所有的key
keys *

# 删除
del key 例: (del name)

# 查看key是否存在 ,存在返回 1, 不存在返回 0
exists key
?
# 查看key类型
type key
运算

set num 1    # 自动识别,字符串里面的 整数
# +1
incr key       例 (incr num)
# -1
decr key       例 (decr num)
?
# +整数
incrby key increment             例 (incrby num 50)
?
# -整数
decrby key increment             例 (decrby num 50)
?
?

list

  • 设置


    # lpush 左添加 (栈)
    lpush key value     #例 lpush my_list a b c d
    ?
    #rpush 右添加   (队列)
    rpush key value     #例 rpush my_rlist a b c d
    ?
  • 查看


    # lrange key start stop
    例 查看所有
    lrange my_list 0 -1
  • 获得list的元素个数


    llen key        #例 llen my_list  
  • 查看位于lindex 位置上的元素


    lindex key index    #例 lindex my_list 3
  • 删除

    • lpop 删除左边第一个


      lpop key        #例 lpop my_list
  • rpop 删除右边第一个


    rpop key        #例 rpop my_list
  • lrem 删除指定


    rpush test_list a a b b c d e a d f m c
    #lrem key count value
    ?
    #count > 0 从左往右 删除数量为count的value
    例 : lrem test_list 2 a
    ?
    #count = 0 删除所有的 value
    例 : lrem test_list 0 a
    ?
    #count < 0 从右往左 删除数量为count的value
    例 : lrem test_list -1 b

hash

  • 设置


    # hset key field value
    例 user { name:lucky}
    hset user name lucky
  • 获取


    # hget key field

    hget user name
  • 删除


    # hdel key field

    hdel user name
  • 设置多个


    #hmset key field value [field value]
    例 user{name:lucky , age:18, sex:male }
    hmset user name lucky age 18 sex male

    ?

  • 获取多个


    # hmget key field field

    hmget user name age

    ?

  • 获取全部field value


    # hgetall key

    hgetall user

    ?

  • 获取所有的field


    # hkeys key

    hkeys user

    ?

  • 获取所有的value


    # hvals key

    hvals user

    ?

  • 获取field的个数


    # hlen key

    hlen user

set

  • 设置


    # sadd key value [value]   (唯一,无序)

    sadd my_set m n b v c x z b
  • 获取


    # smembers key

    smembers my_set
  • 删除

    • srem指定删除


      # srem key member

      srem my_set c
    • spop随机删除


      # spop key

      spop my_set
  • 移动一个集合的值到另一个集合


    # smove oldkey newkey member

    smove my_set my_set2 b
  • 判断集合存在某个值


    # sismember key value

    sismember my_set2 b
  • 交集


    # sinter key1 key2 ..

    sinter my_set my_set2

    把 key1 key2的交集合并到newkey


    # sinterstore newkey key1 key2

    sinterstore new_set my_set my_set2
  • 并集


    # sunion key1 key2 ...

    sunion my_set my_set2
    ?

    把 key1 key2的并集合并到newkey


    # sunionstore newkey key1 key2

    sunionstore new_set2 my_set my_set2
  • 差集


    # sdiff key1 key2
    sdiff my_set my_set2

    把 key1 key2的差集合并到newkey


    # sdiffstore newkey key1 key2

    sdiffstore new_set3 my_set my_set2
  • 获取集合个数


    # scard key

    scard my_set
  • 随机返回一个


    # srandmember key

    srandmember my_set

zset

  • 设置


    # zadd key score member

    zadd my_zset 1 ‘one‘
    zadd my_zset 2 ‘two‘
    zadd my_zset 3 ‘three‘
    zadd my_zset 4 ‘four‘
    zadd my_zset 5 ‘five‘
  • 获取

    • zrange正序


      #zrange key start stop   (withscores)
      zrange my_zset 0 -1  
    • zrevrange倒序


      #zrevrange key start stop
      zrevrange my_zset 0 -1
  • 删除


    #zrem key member
    zrem my_zset two
  • 索引

    • zrank正序


      # zrank key member

      zrank my_zset three
    • zrevrank反序


      # zrevrank key member

      zrevrank my_zset three
  • zcard 查看有序集合元素数


    # zcard key

    zcard my_zset
  • zrangebyscore 返回集合中 score 在给定区间的元素


    # zrange my_zset 0 -1 withscores
    ?
    zrangebyscore my_zset 2 3 withscores
    #返回了 score 在 2~3 区间的元素
  • zcount 返回集合中 score 在给定区间的数量


    # zcount key min max

    zcount my_zset 2 3
  • zscore : 查看score值


    # zscore key member

    zscore my_zset2 a
    ?

    ?

  • zremrangebyrank : 删除集合中排名在给定区间的元素


    # zrange my_zset 0 -1 withscores
    zremrangebyrank my_zset 1 3

    ?

  • zremrangebyscore : 删除集合中 score 在给定区间的元素


    # zrange my_zset 0 -1 withscores
    zremrangebyscore my_zset 1 2

作业

1.练习redis 5种数据类型、及操作 ( 不需要截图上交,自己练习)



 

 

 

 

python操作mysql,redis

python操作mysql

pip install pymysql      


要操作数据库,首先就要建立和数据库的连接,配置pymysql.connect连接数据库:


con = pymysql.connect(
   host = ‘主机‘,
   port = 端口,
   user = ‘用户名‘,
   password = ‘密码‘,
   db = ‘数据库名‘,
   charset = ‘utf8‘
)
?
# 定义游标
cursor = con.cursor()  
cursor.execute(‘show databases‘)        #执行sql
one = cursor.fetchone()              #取出一条数据
all = cursor.fetchall() #取出所有数据
print(one)
print(all)
?
# 插入
row = cursor.execute("insert into test(name,sex) values(%s,%s)", (‘lucky‘,‘male‘))
?
# 更新
row = cursor.execute("update test set name= ‘三花‘ where id = %s", (2,))
?
# 删除
cursor.execute(‘delete from user where id=%s ‘,(1,) )
?
?
# 关闭连接
con.commit()    #提交事物
cursor.close()  #关闭游标
con.close() # 关闭连接
?
?
## 联合查询
?
union = ‘‘‘
select s.name, c.name,d.name from `student` s
left join `select` se on se.s_id = s.s_id
left join course c on se.c_id = c.id
left join department d on s.dept_id = d.id
ORDER BY s.name;
‘‘‘
# cursor.execute(union)
# find =cursor.fetchall()

python操作 redis

pip  install redis



# 创建连接
re = redis.Redis(host=‘127.0.0.1‘, port=‘55555‘)
## 测试
re.set(‘num‘,15)
print(re.get(‘num‘))
?
## set 中文
re.set(‘name‘,‘张三‘)
print(re.get(‘name‘).decode(‘utf8‘) )
?
?
### 大部分的命令 和 redis 中操作一样
不同:
re.ttl()       ### 不能看 负数 -1   -2
re.mset()         ## 用键值对
re.incr()        ## incr   可以加参数,代替了 incrby
re.decr()        ## decr   可以加参数,代替了 decrby
re.lrem()        ## num 放到后面
re.hmset()       # 多插入,用字典
?
?

 

 

 

 

json模块和os模块

 

数据交换JSON

JSON 的基本介绍

JSON全名是JavaScript Object Notation(即:JavaScript对象标记)它是JavaScript的子集。

前端和后端进行数据交互,其实就是JS和Python进行数据交互

json模块 API

接口一:json.dumps(obj) # 将obj这个对象,变成JSON字符串

接口二:json.loads(s) # 将s这个JSON字符串,转换成Python的数据类型

接口三:json.dump

接口四:json.load

JSON注意事项:

1、名称必须用双引号(即:””)来包括

2、值可以是双引号包括的字符串、数字、true、false、null、JavaScript数组,或子对象。

PythonJSON
字典 对象
列表或元组 数组
字符串 字符串
int或float 数字
True或False true或false
None null

 

 

 

文件与路径操作OS模块

os模块 的目录及文件操作

显示当前路径:os.getcwd()

展示当前目录内容:os.listdir(path)

改变当前路径: os.chdir(path)

创建目录: os.mkdir

删除目录: os.rmdir

删除文件: os.remove

重命名: os.rename

os.path 模块的路径操作

路径拼接: os.path.join

所在目录 / 父级目录: os.path.dirname

基本短路径: os.path.basename

绝对路径: os.path.abspath

规范化路径: os.path.normpath

资源大小: os.path.getsize

资源时间: os.path.getctime /

? getatime /

? getmtime

路径是否存在: os.path.exists

是否是存在的目录: os.path.isdir

是否是存在的文件: os.path.isfile

是否是绝对路径: os.path.isabs

 

datetime和logging模块

 

日期与时间

datetime 模块中 主要类:

类名功能说明
date 日期对象,常用的属性有year, month, day
time 时间对象hour,minute,second,毫秒
datetime 日期时间对象,常用的属性有hour, minute, second, microsecond
timedelta 时间间隔,即两个时间点之间的长度

主要使用: datetime.datetiem( ) 、 datetime.timedelta()

日期: datetime.date( year, month, day )

时间: datetime.time( hour, minute, second, microsecond )

日期时间: datetime.datetime( year, month, day,hour, minute, second, microsecond )

时间间隔 : datetime.timedelta( days=0, seconds=0, microseconds=0 milliseconds=0,minutes=0, hours=0, weeks=0 )

datetime.datetime( ) 类中的常用方法:

  1. now ():返回当前日期时间的datetime对象:

  2. utcnow(…):返回当前日期时间的UTC datetime对象

3.strptime(…):解析成datetime对象。根据string, format 2个参数,返回一个对应的datetime对象例子: datetime.datetime.strptime(‘2018-3-22

4.Strftime(): 格式化时间例子:strtime= now.strftime("%Y-%m-%d %H:%M:%S")

5.时间戳转日期时间: t1=datetime.datetime.fromtimestamp(ts)

6日期时间转时间戳:t2=t1.timestamp()

获取datetime对象 中数据:

now = datetime.datetime.now()

小时 分钟 秒钟

now.hour

now.minute

now.second

 

年月日

now.year

now.month

now.day

 

星期

now.weekday()

now.isoweekday()

ISO标准化日期 isocalendar(...) year 年份 week number 周期 weekday星期

a = now.isocalendar()

year = now.isoclendar()[0] 年份

weekNum= now.isoclendar()[1] 周期

weekdaY = now.isoclendar()[2] 星期

 

时间运算 : ( 返回timedelte() 对象)

timedelta类是用来计算二个datetime对象的差值的。 此类中包含如下属性: 1、days:天数 2、microseconds:微秒数 3、seconds:秒数(>=0 并且 <1天)4、total_seconds : 总秒数

logging模块

logging用法

  1. 初始化 logger = logging.getLogger(“name")

  2. 设置级别 logger.setLevel(logging.DEBUG),Logging中有NOTSET < DEBUG < INFO < WARNING < ERROR < CRITICAL这几种级别,日志会记录设置级别以上的日志

  3. 定义Handler,常用的是StreamHandler和FileHandler, StreamHandler:将日志在控制台输出 FileHandler: 将日志记录到文件里面

  4. formatter,定义了log信息的 内容 和格式,例如:‘%(asctime)s %(message)s‘, ‘%Y-%m-%d %H:%M:%S‘

Logging 中几种级别:NOTSET < DEBUG < INFO < WARNING < ERROR < CRITICAL

Formatter 中已有格式 :

%(name)s Logger的名字

%(levelname)s 文本形式的日志级别

%(message)s 用户输出的消息

%(asctime)s 字符串形式的当前时间。默认格式是 “2003-07-08 16:49:45,896”。逗号后面的是毫秒

%(levelno)s 数字形式的日志级别

%(pathname)s 调用日志输出函数的模块的完整路径名,可能没有

%(filename)s 调用日志输出函数的模块的文件名

%(module)s  调用日志输出函数的模块名

%(funcName)s 调用日志输出函数的函数名

%(lineno)d 调用日志输出函数的语句所在的代码行

%(created)f 当前时间,用UNIX标准的表示时间的浮 点数表示

%(relativeCreated)d 输出日志信息时的,自Logger创建以 来的毫秒数

%(thread)d 线程ID。可能没有

%(threadName)s 线程名。可能没有

%(process)d 进程ID。可能没有

 

 

mongobd

mongodb 组织数据的基本形式

进入与退出 数据库

进入 pyvip@Vip:-$: mongo

退出 exit

库、集合操作

库级 管理语句

显示所有库: show dbs

切换数据库: use 数据库名称

查看所在库: db

删除库:db.dropDatabase()

集合 管理语句

查看当前数据库的集合: show collections

集合创建: db.createCollection(name, options)

例子:db.createCollection(‘student‘)

{"ok": 1 }

删除集合:db.集合名称.drop()

数据级 增删查改 操作

插入数据db.集合名称.insert(document)

每一条数据,就是一个document,即 就是一条json

插入文档时,如果不指定_id参数,MongoDB会为文档分配一个唯一的ObjectId

例: db.student.insert({name:‘lucky‘,age:18})

插入多条:

db.student.insert([

? {name:‘jianeng‘,sex:‘male‘,age:18},

? {name:‘张三‘,sex:’female‘,age:30 },

? {name:‘李四‘,sex:’male‘,age:48 },

])

查询数据 db.集合名称.find()

查询所有:db.student.find( )

更新数据db.集合名称.update( <query>, (条件) <update>, {multi: <boolean>} )

全文档更新: db.stu.update({name:’haha’},{xx:’yy’ } )

指定属性更新,通过操作符$set db.student.update({name:‘jianeng‘},{$set:{name:‘xxx‘,age:666 }})

更新多条: { multi: ture } 。 (前提只能指定字段更新) db.student.update( {sex:’feamle‘},{$set:{sex:‘女‘}},{ multi:true} )

删除数据db.集合名称. remove( <query>, (条件) <justOne> )

删除满足条件的,所有数据:db.student.remove({sex:‘女‘})

只是删除 一条db.student.remove({sex:‘女‘},{justOne:true })

Python操作mongodb

连接器(驱动)

安装python包:pip install pymongo

引入包pymongo:import pymongo

建立连接并创建客户端: client= pymongo.MongoClient(‘127.0.0.1‘, 27017)

指定数据库:db=client[ 数据库名 ]

指定集合:stu=db [ 集合名]

 

主要方法:

insert_onei

nsert_many

update_one

update_many

delete_one

delete_many

find_one

find

 

 

传输模型和套接字

传输模型

基本模型

应用程序 应用程序

套接字 套接字

网络设备 网络设备

 

层次的划分

供操作系统或应用进行网络通信的标准接口 应用层 应用层 Telnet FTP SMTP DNS HTTP

将不同的编码方式转换成网络通信中采用的标准表现形式 表现层 应用层 Telnet FTP SMTP DNS HTTP

不同的PC的不同进程之间建立或解除连接,插入同步点 会话层 应用层 Telnet FTP SMTP DNS HTTP

两个主机之间的端对端的数据连接与传输 传输层 传输层 TCP UDP

选择路由并正确的找着目标主机 网络层 网络层 IR ARP RARP ICMP

两个相邻节点之间准确的数据传输 数据链路层 数据链路层 网络通信硬件及接口

原始比特数据流在物理介质上传输 物理层 数据链路层 网络通信硬件及接口

 

传输层说明

说明一: 作为Python开发,咱们都是在应用层的HTTP协议之上进行开发的。

说明二: 网络编程,主要是了解我们Python能编写的最低的层次, 即传输层的基本情况

说明三: HTTP协议是基于TCP之上的 因此我们需要了解TCP连接的基本过程

TCP连接

建立连接(三次握手)

技术分享图片

传输数据

技术分享图片

断开连接(四次挥手)

技术分享图片

IP地址与端口

IP地址

即:电脑说我是:192.168.0.1

端口号

即:电脑上的qq说我的端口是1234

套接字

创建套接字

套接字的基本概念

用户认为的传输:应用逻辑》》》》应用逻辑

实际的传输:套接字》》》套接字

创建套接字实例

import socket
sock = socket.socket()
sock
<socket.socket fd=288, # 文件描述符(唯一标识了一个socket)
			family=AddressFamily.AF_INET,  # AF_INET表示IPv4
			type=SocketKind.SOCK_STREAM,  # SOCK_STREAM表示TCP
			proto=0 >    # 通常都是0


三种套接字

服务端套接字

客户端套接字

对等连接套接字

建立套接字连接

服务端监听套接字的绑定与监听

服务端套接字绑定

>>>import socket
>>>ADDRESS = (‘127.0.0.1‘,8080) # 程序地址是由IP地址 与端口号 共同决定
>>>sock_server = socket.socket()
>>>sock_server.bind(ADDRESS)
>>>sock_server
<socket.socket fd=???, 
			family=AddressFamily.AF_INET, 
			type=SocketKind.SOCK_STREAM,  
			proto=0 
			laddr=(‘127.0.0.1‘,8080)>   
>>>socket_server.close()  #和文件一样,socket用完了一定要关闭


>>>import socket
>>>sock_client = socket.socket()
>>>sock_client
<socket.socket fd=???, 
			family=AddressFamily.AF_INET, 
			type=SocketKind.SOCK_STREAM,  
			proto=0>  # 注意!此时这个客户端套接字还并不拥有自己的ADDRESS 
>>>socket_client.connect((‘127.0.0.1‘,8080)) # 连接到服务端套接字bind的ADDRESS元组
Traceback(most recent call last):
File"<stdin>",line 1, in<module>
ConnectionRefusedError:[WinError 10061] 由于目标计算机积极拒绝,无法连接     # 之所以服务端计算机会拒绝连接,是因为服务端套接字还没有开始监听


服务端套接字的监听

>>>sock_server.listen(5)
>>>socket_client.connect((‘127.0.0.1‘,8080)
>>>sock_client
<socket.socket fd=???, 
			family=AddressFamily.AF_INET, 
			type=SocketKind.SOCK_STREAM,  
			proto=0
			laddr=(‘127.0.0.1‘,1951), # 可以看到,连接后的客户端套接字被随机分配了一个ADDRESS
			raddr=(‘127.0.0.1‘,8080)> # 并且这里也有对应的服务端套接字地址
>>>sock_server
<socket.socket fd=???, 
			family=AddressFamily.AF_INET, 
			type=SocketKind.SOCK_STREAM,  
			proto=0
			laddr=(‘127.0.0.1‘,8080)> # 没有addr 


客户端套接字的主动连接

 

服务端接受连接并生成对等的连接套接字

>>>res = sock_server.accept()   # 接受连接请求
>>>type(res)   # 返回一个元组
<class ‘tuple‘>
>>>coon. addr = res
>>>coon     # 元组第一项是一个对等的连接套接字
<socket.socket fd=320, 
			family=AddressFamily.AF_INET, 
			type=SocketKind.SOCK_STREAM,  
			proto=0
			laddr=(‘127.0.0.1‘,8080), 
			raddr=(‘127.0.0.1‘,1951)> # 远端对等套接字地址
>>>addr   # 元组第二项是对等套接字地址元组
(‘127.0.0.1‘,1951)
>>>coon is sock_server
False  # 对等的连接套接字与服务端套接字是不同的套接字


accept阻塞说明

使用套接字传输数据

1、客户端发送请求数据到服务端

2、服务端接受来自客户端的请求数据

3、服务端向客户端返回响应数据

4、客户端接受来自服务端的响应

套接字传输

>>>sock_client.send(b‘hello my server!‘) # 只能发送 bytes类型
16   # 返回发送出去的字节数

	>>>coon.recv(1024) # 指明一次性能接收到的最大的字节数量
	b‘hello my server!‘
>>>coon.send(b‘i am here!‘)
10
        >>>sock_client.recv(1024)
        b‘i am here!‘


 

断开套接字连接

客户端主动断开连接

>>>sock_client.close()
		>>>coon.recv(1024)
		b‘‘
		>>>coon.close()


 

服务端判断客户端断开连接

 

 

非阻塞套接字和IO多路复用

基本IO模型

普通套接字实现的服务端的缺陷:一次只能服务一个客户端

普通套接字实现的服务端的瓶颈:1、在没有新的套接字来之前,不能处理已经建立的套接字的请求。2、在没有接受到客户端请求数据之前,不能与其他客户端建立连接

import socket
server = socket.socket()
server.bind((‘0.0.0.0‘,8080))
server.listen(5)
while True:
connection,raddr = server.accept() #阻塞
while True:
recv_data = coonection.recv(1024) #阻塞
if recv_data:
print(recv_data)
connection.send(recv_data)
else:
connection.close()
break

 

非阻塞套接字

 

 

非阻塞IO模型

技术分享图片

 

使用非阻塞套接字实现并发处理

整体思路:宁可while True,也不要阻塞


connection_list = []
while True: # 第一层循环只负责生成对等连接套接字
try:
connection,remote_address = server.accept()
connection.setblocking(False)
print("Accept:{raddr}".format(raddr=remote_address))
connection_list.append(connection) #保留已生成的对等连接套接字
except BlockingIOError as error:
pass

for connection in connection_list: # 把所有已生成的对等连接套接字都处理一遍
try:
recv_data = connection.recv(1024)
if recv_data:
print("Received from {raddr}: {data}".format(raddr=connection.getpeername(),data=recv_data.decode(‘utf-8‘)))
connection.send(b‘Recved:%s‘ % recv_data)
else:
print(‘Disconnect from {raddr}‘.format(raddr=connection.getpeername()))
connection.close()
connection_list.remove(connection)
except BlockingIOError as error: #成功处理完一个对等连接套接字,就移出一个
pass

 

 

 

 

多进程和多线程

第七节 线程和进程

线程:

线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一个线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程执行不同的任务。

? 进程:一个程序执行的实例,可以称为一个进程,第一个进程有一个PID(进程标识符),进程具有不同的优先级,第一个进程至少有一个线程,进程里面的第一个线程称为主线程,其它的线程通过主线程创建,称为子线程,子线程可以继续创建子线程,子线程之间没有隶属关系,各子线程之间是独立的,每一个程序的内存是独立的。这样的一个程序就叫做进程。比如,QQ是一个进程,画图程序是一个进程,里面包含对各种资源的调用。

? 线程:是操作系统最小的调度单位,是一串指令的集合。进程要操作CPU,必须要先创建一个线程,所有在同一个进程里的线程,是共享同一块内存空间的

 

进程和线程之间的区别:

? 1、线程共享内存空间,进程是独立的内存空间。

? 2、线程可以直接访问数据段,多个子进程之间的数据是独立的,子进程之间不能直接访问。

? 3、同一个进程的线程之间可以直接交流(信息的传递,数据的共享),两个进程想通信,必须通过一个中间代理来实现。

? 4、创建新线程很简单,创建新进程,需要对其父进程进行一次克隆。

? 5、一个线程可以控制和操作同一进程里的其它线程,但是进程只能操作子进程,不能操作其它的的进程。

? 6、对于主线程的修改,有可能会影响到其它线程的运行,但一个父进程的修改,不会影响其它子进程的运行

线程应用示例1:

import threading
import time
?
?
class MyThread(threading.Thread):
   def __init__(self, n, sleep_time):
       super().__init__()
       self.n = n
       self.sleep_time = sleep_time
?
   def run(self):
       print("Running Task ", self.n)
       time.sleep(self.sleep_time)
       print("task %s done." % self.n)
?
?
t_job = []
start_time = time.time()
for i in range(50):
   t = MyThread("t-%s" % i, 3)
   t.start()
   t_job.append(t)
?
for t in t_job:
   t.join()
?
print("main thread cost:%s " % (time.time() - start_time))
守护线程:

import threading
import time
?
?
class MyThread(threading.Thread):
   def __init__(self, n, sleep_time):
       super().__init__()
       self.n = n
       self.sleep_time = sleep_time
?
   def run(self):
       print("Running Task ", self.n)
       time.sleep(self.sleep_time)
       print("task %s done. %s " % (self.n,threading.current_thread()))
?
?
t_job = []
start_time = time.time()
for i in range(50):
   t = MyThread("t-%s" % i, 3)
   t.setDaemon(True) #把当前线程设置为守护线程
   t.start()
   t_job.append(t)
?
time.sleep(5)
print("main thread cost:%s %s" % (time.time() - start_time,threading.current_thread()))
?

? 每个子线程都设置为守护线程,主线程一结束,其它的子线程也跟着结束,不管子线程执行完毕没有。

? time.sleep(5),主线程休眠5秒,所有子线程都可以执行完毕,去掉此语句,主线程一结束,子线程全部结束

GIL 全局解释器锁

? 在python中,不管CPU有多少核,在同一时间,只有一个线程在执行,一切并行都是假象

? python的线程是调用操作系统的原生线程

? Python代码的执行由Python 虚拟机(也叫解释器主循环,CPython版本)来控制,Python 在设计之初就考虑到要在解释器的主循环中,同时只有一个线程在执行,即在任意时刻,只有一个线程在解释器中运行。对Python 虚拟机的访问由全局解释器锁(GIL)来控制,正是这个锁能保证同一时刻只有一个线程在运行。

在多线程环境中,Python 虚拟机按以下方式执行:

? 1. 设置GIL 2. 切换到一个线程去运行 3. 运行: a. 指定数量的字节码指令,或者

b. 线程主动让出控制(可以调用time.sleep(0)) 4. 把线程设置为睡眠状态 5. 解锁GIL 6. 再次重复以上所有步骤

在调用外部代码(如C/C++扩展函数)的时候,GIL 将会被锁定,直到这个函数结束为止(由于在这期间没有Python 的字节码被运行,所以不会做线程切换)。

全局解释器锁GIL设计理念与限制

? GIL的设计简化了CPython的实现,使得对象模型,包括关键的内建类型如字典,都是隐含可以并发访问的。锁住全局解释器使得比较容易的实现对多线程的支持,但也损失了多处理器主机的并行计算能力。

但是,不论标准的,还是第三方的扩展模块,都被设计成在进行密集计算任务是,释放GIL。

还有,就是在做I/O操作时,GIL总是会被释放。对所有面向I/O 的(会调用内建的操作系统C 代码的)程序来说,GIL 会在这个I/O 调用之前被释放,以允许其它的线程在这个线程等待I/O 的时候运行。如果是纯计算的程序,没有 I/O 操作,解释器会每隔 100 次操作就释放这把锁,让别的线程有机会执行(这个次数可以通过 sys.setcheckinterval 来调整)如果某线程并未使用很多I/O 操作,它会在自己的时间片内一直占用处理器(和GIL)。也就是说,I/O 密集型的Python 程序比计算密集型的程序更能充分利用多线程环境的好处。

Pypy可以实现真正的多线程,Jpy也可以实现真正的多线程

?

线程锁(互斥锁Mutex)

? 线程在进程之下执行,一个进程下可以运行多个线程,它们之间共享相同上下文。线程包括开始、执行顺序和结束三部分。它有一个指针,用于记录当前运行的上下文。当其它线程执行时,它可以被抢占(中断)和临时挂起(也称睡眠) ——这种做法叫做 让步(yielding)

? 一个进程中的各个线程与主进程共享同一片数据空间,与独立进程相比,线程之间信息共享和通信更加容易。线程一般以并发执行,正是由于这种并发和数据共享机制,使多任务间的协作成为可能。当然,这种共享也并不是没有风险的,如果多个线程访问同一数据空间,由于访问顺序不同,可能导致结果不一致,这种情况通常称为竞态条件(race condition),不过大多数线程库都有同步原语,以允许线程管理器的控制执行和访问;另一个要注意的问题是,线程无法给予公平执行时间,CPU 时间分配会倾向那些阻塞更少的函数。

线程锁(互斥锁Mutex)

? 一个进程以可以启动多个线程,多个线程共享父进程的内存空间,也就意味着每个线程可以访问同一份数据,此时,如果2个线程要同时修改一份数据,会出现什么状况?

递归锁

import threading, time
?
num = 0
num2 = 0
?
?
def run1():
   print("grab the first part data")
   lock.acquire()
   global num
   num += 1
   lock.release()
   return num
?
?
def run2():
   print("grab the second part data")
   lock.acquire()
   global num2
   num2 += 1
   lock.release()
?
?
def run3():
   lock.acquire()
   res = run1()
   print("---------------between run1 and run2--------------")
   res2 = run2()
   lock.release()
   print(res,res2)
?
?
lock = threading.RLock()
for i in range(10):
   t = threading.Thread(target=run3)
   t.start()
?
while threading.active_count() != 1:
   print(threading.active_count())
else:
   print("---------------all threads done.------")
   print(num, num2)
?
Semaphore(信号量)

? 互斥锁同时只允许一个线程更改数据,而Semaphore 是同时允许一定数量的线程更改数据,比如厕所有3个坑,那最多只允许3个人上厕所,后面的人只能等待里面有人出来了才能进去。


import threading,time
?
def run(n):
   semaphore.acquire()
   time.sleep(1)
   print("run the thread: %s\n" %n)
   semaphore.release()
?
if __name__==‘__main__‘:
   num=0
   semaphore=threading.BoundedSemaphore(5) #最多允许5个线程同时运行
   for i in range(22):
       t=threading.Thread(target=run,args=(i,))
       t.start()
?
while threading.active_count()!=1:
   pass
else:
   print("----all threads done------")
   print(num)
Events

? 通过Event来实现两个或多个线程间的交互,下面是一个红绿灯的例子,即启动一个线程做交通指挥灯,生成几个线程做车辆,车辆行驶按红灯停,绿灯行的规则

? a server thread can set or rest it.

? event.set()

? event.clear()

? If the flag is set,the wait method doesn‘t do anything. 标志位设定了,代表绿灯,直接通行。

? If the flag was cleared,wait will block until it becomes set again.标志位被清空,代表红灯,等待变绿

? Any number of threads may wait for thd same event.


import threading, time
?
event = threading.Event()
event.set() #设置标志位,表示现在是绿灯
?
?
def lighter():
   count = 0
   while True:
       if count > 5 and count < 10:  # 改成红灯
           event.clear()  # 把标志位清空
           print("\033[41;1mred light is on ...\033[0m")
       elif count > 10:
           event.set()  # 变绿灯
           count = 0
       else:
           print("\033[42;1mgreen light is on ...\033[0m")
       time.sleep(1)
       count += 1
?
?
def car(name):
   while True:
       if event.is_set():  # 标志符设置,代表绿灯亮,汽车通行
           print("[%s] running......" % name)
           time.sleep(1)
       else:
           print("[%s] sees red light,waithing....." % name)
           event.wait()
           print("[%s] green light is on ,start going......" % name)
?
?
light = threading.Thread(target=lighter, )
light.start()
car1 = threading.Thread(target=car, args=("Tesla",))
car1.start()

 

queue队列(非常重要的知识点)

? queue is especially useful in threaded programming when information must be exchanged safely between multiple threads.

? 1、生产者和消费者模型

? 在并发编程中使用生产者和消费者模式通解解决大多数并发问题。该模式通过平衡生产线程和消费线程的工作能力来提高程序的整体处理数据的速度。

? 2、为什么要使用生产者和消费者模式

? 在线程世界里,生产者就是产生数据的线程,消费者就是消费数据的纯种。在多线程并发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这个问题于是引入了生产者和消费者模式。

? 3、什么是生产者和消费者模式

? 生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者来处理,直接扔给阻塞队列;消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者与消费者的处理能力。

? 4、生产者和消费者模式示例代码:


import threading,time,queue
?
q=queue.Queue(maxsize=10)
def Producer(name):
   count=1
   while True:
       q.put("骨头 %s" %count)
       print("生产了骨头",count)
       count+=1
       time.sleep(0.5)
?
def Consumer(name):
   #while q.qsize()>0:
   while True:
       print("[%s] 取到[%s],并且吃了它......"%(name,q.get()))
       time.sleep(1)
?
p=threading.Thread(target=Producer,args=("Alex",))
c=threading.Thread(target=Consumer,args=("Chenronghua",))
c1=threading.Thread(target=Consumer,args=("王森",))
p.start()
c.start()
c1.start()

 

threading 模块

threading 是 Python 高级别的多线程模块。

threading 模块的函数
  • active_count() 当前活动的 Thread 对象个数

  • current_thread() 返回当前 Thread 对象

  • get_ident() 返回当前线程

  • enumerater() 返回当前活动 Thread 对象列表

  • main_thread() 返回主 Thread 对象

  • settrace(func) 为所有线程设置一个 trace 函数

  • setprofile(func) 为所有线程设置一个 profile 函数

  • stack_size([size]) 返回新创建线程栈大小;或为后续创建的线程设定栈大小为 size

  • TIMEOUT_MAX Lock.acquire(), RLock.acquire(), Condition.wait() 允许的最大值

threading 可用对象列表:

  • Thread 表示执行线程的对象

  • Lock 锁原语对象

  • RLock 可重入锁对象,使单一进程再次获得已持有的锁(递归锁)

  • Condition 条件变量对象,使得一个线程等待另一个线程满足特定条件,比如改变状态或某个值

  • Semaphore 为线程间共享的有限资源提供一个”计数器”,如果没有可用资源会被阻塞

  • Event 条件变量的通用版本,任意数量的线程等待某个时间的发生,在改事件发生后所有线程被激活

  • Timer 与 Thread 相识,不过它要在运行前等待一段时间

  • Barrier 创建一个”阻碍”,必须达到指定数量的线程后才可以继续

Thread 类

Thread 对象的属性有:Thread.nameThread.identThread.daemon。详见(The Python Standard Library)

Thread 对象方法:Thread.start()Thread.run()Thread.join(timeout=None)Thread.getNameThread.setNameThread.is_alive()Thread.isDaemon()Thread.setDaemon()。详见(The Python Standard Library)

使用 Thread 类,可以有很多种方法来创建线程,这里使用常见的两种:

  • 创建 Thread 实例,传给它一个函数。

  • 派生 Thread 子类,并创建子类的实例。

     

 

 

 

 

 

 

进程和线程的补充

 

 

 

 

 

 

 

并发通信

 

 

 

 

 

 

进程池和线程池

 

 

 

 

 

 

协程

python数据库进阶

标签:不用   了解   ide   nts   系统   最大的   不一致   生产者消费者   倒序   

原文地址:https://www.cnblogs.com/wangshixiong576422/p/9710535.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!