Python操作RabbitMQ、Redis、Memcache、SQLAlchemy
SQLAlchemy
Python 的 ORM 框架 SQLAlchemy ,Models是Django自带的ORM框架,也正是因为是Django原生的,所以兼容性远远不如SQLAlchemy
1.创建数据表
1 # ORM中的数据表是什么呢? 2 # Object Relation Mapping 3 # Object - Table 通过 Object 去操纵数据表 4 # 从而引出了我们的第一步创建数据表 - 创建Object 5 # 1. 创建Object 6 # class User(object): 7 # pass 8 9 # 2. 让Object与数据表产生某种关系 也就是让Object与数据表格式极度相似 10 # 导入官宣基础模型 11 from sqlalchemy.ext.declarative import declarative_base 12 # 实例化官宣模型 - Base 就是 ORM 模型 13 Base = declarative_base() 14 # 当前的这个Object继承了Base也就是代表了Object继承了ORM的模型 15 class User(Base): # 相当于 Django Models中的 Model 16 # 为Table创建名称 17 __tablename__ = "user" 18 # 创建ID数据字段 , 那么ID是不是一个数据列呢? 也就是说创建ID字段 == 创建ID数据列 19 from sqlalchemy import Column,Integer,String 20 # id = Column(数据类型,索引,主键,外键,等等) 21 # int == Integer 22 id = Column(Integer,primary_key=True,autoincrement=True) 23 # str == char(长度) == String(长度) 24 name = Column(String(32),index=True) 25 26 # 3.去数据库中创建数据表? or 先连接数据库? 27 # 3.去连接数据库 创建数据引擎 28 from sqlalchemy import create_engine 29 # 创建的数据库引擎 30 engine = create_engine("mysql+pymysql://root:DragonFire@127.0.0.1:3306/dragon?charset=utf8") 31 32 # Base 自动检索所有继承Base的ORM 对象 并且创建所有的数据表 33 Base.metadata.create_all(engine)
2.增删改查操作
2.1.增加数据
1 #insert 为数据表增加数据 2 # insert One 增加一行数据 3 # insert into user(name) values ("DragonFire") 4 # 在ORM中的操作: 5 # 1.首先导入之间做好的ORM 对象 User 6 from my_create_table import User 7 # 2.使用Users ORM模型创建一条数据 8 user1 = User(name="DragonFire") 9 # 数据已经创建完了,但是需要写入到数据库中啊,怎么写入呢? 10 # 3.写入数据库: 11 # 首先打开数据库会话 , 说白了就是创建了一个操纵数据库的窗口 12 # 导入 sqlalchemy.orm 中的 sessionmaker 13 from sqlalchemy.orm import sessionmaker 14 # 导入之前创建好的 create_engine 15 from my_create_table import engine 16 # 创建 sessionmaker 会话对象,将数据库引擎 engine 交给 sessionmaker 17 Session = sessionmaker(engine) 18 # 打开会话对象 Session 19 db_session = Session() 20 # 在db_session会话中添加一条 UserORM模型创建的数据 21 db_session.add(user1) 22 # 使用 db_session 会话提交 , 这里的提交是指将db_session中的所有指令一次性提交 23 db_session.commit() 24 25 # 当然也你也可很任性的提交多条数据 26 # 方法一: 27 user2 = User(name="Dragon") 28 user3 = User(name="Fire") 29 db_session.add(user2) 30 db_session.add(user3) 31 db_session.commit() 32 # 之前说过commit是将db_session中的所有指令一次性提交,现在的db_session中至少有两条指令user2和user3 33 db_session.close() 34 #关闭会话 35 36 # 如果说你觉得方法一很麻烦,那么方法二一定非常非常适合你 37 # 方法二: 38 user_list = [ 39 User(name="Dragon1"), 40 User(name="Dragon2"), 41 User(name="Dragon3") 42 ] 43 db_session.add_all(user_list) 44 db_session.commit() 45 46 db_session.close()
2.2.查询数据
1 # ORM操作查询数据 2 # 有了刚才Insert增加数据的经验,那么查询之前的准备工作,就不用再重复了吧 3 # 回想一下刚才Insert时我们的操作 4 from my_create_table import User, engine 5 from sqlalchemy.orm import sessionmaker 6 7 Session = sessionmaker(engine) 8 db_session = Session() 9 10 # 1. select * from user 查询user表中的所有数据 11 # 语法是这样的 使用 db_session 会话 执行User表 query(User) 取出全部数据 all() 12 user_all_list = db_session.query(User).all() 13 print(user_all_list) # [<my_create_table.User object at 0x0000016D7C4BCDD8>] 14 # 如何查看user_all_list其中的数据呢? 循环呗 15 for i in user_all_list: 16 print(i.id, i.name) # ORM对象 直接使用调用属性的方法 拿出对应字段的值 17 18 db_session.close() 19 #关闭会话 20 21 # 2. select * from user where id >= 20 22 # 语法是这样的 使用 db_session 会话 执行User表 query(User) 筛选内容User.id >=20 的数据全部取出 all() 23 user_all_list = db_session.query(User).filter(User.id >= 20).all() 24 print(user_all_list) 25 for i in user_all_list: 26 print(i.id, i.name) 27 28 db_session.close() 29 #关闭会话 30 31 # 3. 除了取出全部还可以只取出一条 32 user = db_session.query(User).filter(User.id >= 20).first() 33 print(user.id, user.name) 34 db_session.close() 35 #关闭会话 36 37 # 4. 乌龙 之 忘了取出数据....... 38 wulong1 = db_session.query(User).filter(User.id >= 20) 39 print(wulong1) 40 #SELECT user.id AS user_id, user.name AS user_name 41 #FROM user 42 #WHERE user.id >= %(id_1)s 43 # Fuck我忘了取出数据了!!!!!!! 哎? wulong1给我显示了原生SQL语句,因祸得福了 44 wulong2 = db_session.query(User) 45 print(wulong2) 46 #SELECT user.id AS user_id, user.name AS user_name 47 #FROM user 48 # Fuck我又忘了取出数据了!!!!!!! 哎? wulong2给我显示了原生SQL语句,因祸得福了 49 db_session.close() 50 #关闭会话
2.3.修改数据
1 # ORM更新数据 2 # 无论是更新还是删除,首先要做的事情,就应该是查询吧 3 # 根据之前原有的经验,接下来是不是要导入ORM对象了,是不是要创建db_session会话了 4 from my_create_table import User,engine 5 from sqlalchemy.orm import sessionmaker 6 Session = sessionmaker(engine) 7 db_session = Session() 8 9 # UPDATE user SET name="NBDragon" WHERE id=20 更新一条数据 10 # 语法是这样的 : 11 # 使用 db_session 执行User表 query(User) 筛选 User.id = 20 的数据 filter(User.id == 20) 12 # 将name字段的值改为NBDragon update({"name":"NBDragon"}) 13 res = db_session.query(User).filter(User.id == 20).update({"name":"NBDragon"}) 14 print(res) # 1 res就是我们当前这句更新语句所更新的行数 15 # 注意注意注意 16 # 这里一定要将db_session中的执行语句进行提交,因为你这是要对数据中的数据进行操作 17 # 数据库中 增 改 删 都是操作,也就是说执行以上三种操作的时候一定要commit 18 db_session.commit() 19 db_session.close() 20 #关闭会话 21 22 # 更新多条 23 res = db_session.query(User).filter(User.id <= 20).update({"name":"NBDragon"}) 24 print(res) # 6 res就是我们当前这句更新语句所更新的行数 25 db_session.commit() 26 db_session.close() 27 #关闭会话
2.4.删除数据
1 # ORM 删除一条多条数据 2 # 老规矩 3 # 导入 ORM 创建会话 4 from my_create_table import User,engine 5 from sqlalchemy.orm import sessionmaker 6 Session = sessionmaker(engine) 7 db_session = Session() 8 9 # DELETE FROM `user` WHERE id=20 10 res = db_session.query(User).filter(User.id==20).delete() 11 print(res) 12 # 是删除操作吧,没错吧,那你想什么呢?commit吧 13 db_session.commit() 14 15 db_session.close() 16 #关闭会话
2.5.搞基!高级版查询操作
1 # 高级版查询操作,厉害了哦 2 #老规矩 3 from my_create_table import User,engine 4 from sqlalchemy.orm import sessionmaker 5 6 Session = sessionmaker(engine) 7 db_session = Session() 8 9 # 查询数据表操作 10 # and or 11 from sqlalchemy.sql import and_ , or_ 12 ret = db_session.query(User).filter(and_(User.id > 3, User.name == 'DragonFire')).all() 13 ret = db_session.query(User).filter(or_(User.id < 2, User.name == 'DragonFire')).all() 14 15 # 查询所有数据 16 r1 = db_session.query(User).all() 17 18 # 查询数据 指定查询数据列 加入别名 19 r2 = db_session.query(User.name.label('username'), User.id).first() 20 print(r2.id,r2.username) # 15 NBDragon 21 22 # 表达式筛选条件 23 r3 = db_session.query(User).filter(User.name == "DragonFire").all() 24 25 # 原生SQL筛选条件 26 r4 = db_session.query(User).filter_by(name='DragonFire').all() 27 r5 = db_session.query(User).filter_by(name='DragonFire').first() 28 29 # 字符串匹配方式筛选条件 并使用 order_by进行排序 30 r6 = db_session.query(User).filter(text("id<:value and name=:name")).params(value=224, name='DragonFire').order_by(User.id).all() 31 32 #原生SQL查询 33 r7 = db_session.query(User).from_statement(text("SELECT * FROM User where name=:name")).params(name='DragonFire').all() 34 35 # 筛选查询列 36 # query的时候我们不在使用User ORM对象,而是使用User.name来对内容进行选取 37 user_list = db_session.query(User.name).all() 38 print(user_list) 39 for row in user_list: 40 print(row.name) 41 42 # 别名映射 name as nick 43 user_list = db_session.query(User.name.label("nick")).all() 44 print(user_list) 45 for row in user_list: 46 print(row.nick) # 这里要写别名了 47 48 # 筛选条件格式 49 user_list = db_session.query(User).filter(User.name == "DragonFire").all() 50 user_list = db_session.query(User).filter(User.name == "DragonFire").first() 51 user_list = db_session.query(User).filter_by(name="DragonFire").first() 52 for row in user_list: 53 print(row.nick) 54 55 # 复杂查询 56 from sqlalchemy.sql import text 57 user_list = db_session.query(User).filter(text("id<:value and name=:name")).params(value=3,name="DragonFire") 58 59 # 查询语句 60 from sqlalchemy.sql import text 61 user_list = db_session.query(User).filter(text("select * from User id<:value and name=:name")).params(value=3,name="DragonFire") 62 63 # 排序 : 64 user_list = db_session.query(User).order_by(User.id).all() 65 user_list = db_session.query(User).order_by(User.id.desc()).all() 66 for row in user_list: 67 print(row.name,row.id) 68 69 #其他查询条件 70 """ 71 ret = session.query(User).filter_by(name='DragonFire').all() 72 ret = session.query(User).filter(User.id > 1, User.name == 'DragonFire').all() 73 ret = session.query(User).filter(User.id.between(1, 3), User.name == 'DragonFire').all() # between 大于1小于3的 74 ret = session.query(User).filter(User.id.in_([1,3,4])).all() # in_([1,3,4]) 只查询id等于1,3,4的 75 ret = session.query(User).filter(~User.id.in_([1,3,4])).all() # ~xxxx.in_([1,3,4]) 查询不等于1,3,4的 76 ret = session.query(User).filter(User.id.in_(session.query(User.id).filter_by(name='DragonFire'))).all() 子查询 77 from sqlalchemy import and_, or_ 78 ret = session.query(User).filter(and_(User.id > 3, User.name == 'DragonFire')).all() 79 ret = session.query(User).filter(or_(User.id < 2, User.name == 'DragonFire')).all() 80 ret = session.query(User).filter( 81 or_( 82 User.id < 2, 83 and_(User.name == 'eric', User.id > 3), 84 User.extra != "" 85 )).all() 86 # select * from User where id<2 or (name="eric" and id>3) or extra != "" 87 88 # 通配符 89 ret = db_session.query(User).filter(User.name.like('e%')).all() 90 ret = db_session.query(User).filter(~User.name.like('e%')).all() 91 92 # 限制 93 ret = db_session.query(User)[1:2] 94 95 # 排序 96 ret = db_session.query(User).order_by(User.name.desc()).all() 97 ret = db_session.query(User).order_by(User.name.desc(), User.id.asc()).all() 98 99 # 分组 100 from sqlalchemy.sql import func 101 102 ret = db_session.query(User).group_by(User.extra).all() 103 ret = db_session.query( 104 func.max(User.id), 105 func.sum(User.id), 106 func.min(User.id)).group_by(User.name).all() 107 108 ret = db_session.query( 109 func.max(User.id), 110 func.sum(User.id), 111 func.min(User.id)).group_by(User.name).having(func.min(User.id) >2).all() 112 """ 113 114 # 关闭连接 115 db_session.close()
2.6.高级修改数据操作
1 #高级版更新操作 2 from my_create_table import User,engine 3 from sqlalchemy.orm import sessionmaker 4 5 Session = sessionmaker(engine) 6 db_session = Session() 7 8 #直接修改 9 db_session.query(User).filter(User.id > 0).update({"name" : "099"}) 10 11 #在原有值基础上添加 - 1 12 db_session.query(User).filter(User.id > 0).update({User.name: User.name + "099"}, synchronize_session=False) 13 14 #在原有值基础上添加 - 2 15 db_session.query(User).filter(User.id > 0).update({"age": User.age + 1}, synchronize_session="evaluate") 16 db_session.commit()
3.一对多的操作 : ForeignKey
3.1.创建数据表及关系relationship:
1 from sqlalchemy.ext.declarative import declarative_base 2 3 Base = declarative_base() 4 5 # 这次我们要多导入一个 ForeignKey 字段了,外键关联对了 6 from sqlalchemy import Column,Integer,String,ForeignKey 7 # 还要从orm 中导入一个 relationship 关系映射 8 from sqlalchemy.orm import relationship 9 10 class ClassTable(Base): 11 __tablename__="classtable" 12 id = Column(Integer,primary_key=True) 13 name = Column(String(32),index=True) 14 15 class Student(Base): 16 __tablename__="student" 17 id=Column(Integer,primary_key=True) 18 name = Column(String(32),index=True) 19 20 # 关联字段,让class_id 与 class 的 id 进行关联,主外键关系(这里的ForeignKey一定要是表名.id不是对象名) 21 class_id = Column(Integer,ForeignKey("classtable.id")) 22 23 # 将student 与 classtable 创建关系 这个不是字段,只是关系,backref是反向关联的关键字 24 to_class = relationship("ClassTable",backref = "stu2class") 25 26 from sqlalchemy import create_engine 27 engine = create_engine("mysql+pymysql://root:DragonFire@127.0.0.1:3306/dragon?charset=utf8") 28 29 Base.metadata.create_all(engine)
3.2.基于relationship增加数据
1 from my_ForeignKey import Student, ClassTable,engine 2 # 创建连接 3 from sqlalchemy.orm import sessionmaker 4 # 创建数据表操作对象 sessionmaker 5 DB_session = sessionmaker(engine) 6 db_session = DB_session() 7 8 # 增加数据 9 # 1.简单增加数据 10 # 添加两个班级: 11 # db_session.add_all([ 12 # ClassTable(name="OldBoyS1"), 13 # ClassTable(name="OldBoyS2") 14 # ]) 15 # db_session.commit() 16 # 添加一个学生 DragonFire 班级是 OldBoyS1 17 # 查询要添加到的班级 18 # class_obj = db_session.query(ClassTable).filter(ClassTable.name == "OldBoyS1").first() 19 # 创建学生 20 # stu = Student(name="DragonFire",class_id = class_obj.id) 21 # db_session.add(stu) 22 # db_session.commit() 23 24 # 2. relationship版 添加数据 25 # 通过关系列 to_class 可以做到两件事 26 # 第一件事 在ClassTable表中添加一条数据 27 # 第二件事 在Student表中添加一条数据并将刚刚添加的ClassTable的数据id填写在Student的class_id中 28 # stu_cla = Student(name="DragonFire",to_class=ClassTable(name="OldBoyS1")) 29 # print(stu_cla.name,stu_cla.class_id) 30 # db_session.add(stu_cla) 31 # db_session.commit() 32 33 # 3.relationship版 反向添加数据 34 # 首先建立ClassTable数据 35 class_obj = ClassTable(name="OldBoyS2") 36 # 通过class_obj中的反向关联字段backref - stu2class 37 # 向 Student 数据表中添加 2条数据 并将 2条数据的class_id 写成 class_obj的id 38 # class_obj.stu2class = [Student(name="BMW"),Student(name="Audi")] 39 # db_session.add(class_obj) 40 # db_session.commit() 41 42 # 关闭连接 43 db_session.close()
3.3.基于relationship查询数据
1 from my_ForeignKey import Student, ClassTable,engine 2 3 from sqlalchemy.orm import sessionmaker 4 DB_session = sessionmaker(engine) 5 db_session = DB_session() 6 7 # 1.查询所有数据,并显示班级名称,连表查询 8 student_list = db_session.query(Student).all() 9 for row in student_list: 10 # row.to_class.name 通过Student对象中的关系字段relationship to_class 获取关联 ClassTable中的name 11 print(row.name,row.to_class.name,row.class_id) 12 13 # 2.反向查询 14 class_list = db_session.query(ClassTable).all() 15 for row in class_list: 16 for row2 in row.stu2class: 17 print(row.name,row2.name) 18 # row.stu2class 通过 backref 中的 stu2class 反向关联到 Student 表中根据ID获取name 19 20 21 db_session.close()
3.4.更新数据
1 from my_ForeignKey import Student, ClassTable,engine 2 3 from sqlalchemy.orm import sessionmaker 4 DB_session = sessionmaker(engine) 5 db_session = DB_session() 6 7 # 更新 8 class_info = db_session.query(ClassTable).filter(ClassTable.name=="OldBoyS1").first() 9 db_session.query(Student).filter(Student.class_id == class_info.id).update({"name":"NBDragon"}) 10 db_session.commit() 11 12 db_session.close()
3.5.删除数据
1 from my_ForeignKey import Student, ClassTable,engine 2 3 from sqlalchemy.orm import sessionmaker 4 DB_session = sessionmaker(engine) 5 db_session = DB_session() 6 7 # 删除 8 class_info = db_session.query(ClassTable).filter(ClassTable.name=="OldBoyS1").first() 9 db_session.query(Student).filter(Student.class_id == class_info.id).delete() 10 db_session.commit() 11 12 db_session.close()
4.多对多 : ManyToMany
4.1.创建表及关系
1 from sqlalchemy.ext.declarative import declarative_base 2 3 Base = declarative_base() 4 5 from sqlalchemy import Column,Integer,String,ForeignKey 6 from sqlalchemy.orm import relationship 7 8 class Hotel(Base): 9 __tablename__="hotel" 10 id=Column(Integer,primary_key=True) 11 girl_id = Column(Integer,ForeignKey("girl.id")) 12 boy_id = Column(Integer,ForeignKey("boy.id")) 13 14 class Girl(Base): 15 __tablename__="girl" 16 id=Column(Integer,primary_key=True) 17 name = Column(String(32),index=True) 18 19 #创建关系 20 boys = relationship("Boy",secondary="hotel",backref="girl2boy") 21 22 23 class Boy(Base): 24 __tablename__="boy" 25 id=Column(Integer,primary_key=True) 26 name = Column(String(32),index=True) 27 28 29 from sqlalchemy import create_engine 30 engine = create_engine("mysql+pymysql://root:DragonFire@127.0.0.1:3306/dragon?charset=utf8") 31 32 Base.metadata.create_all(engine)
4.2.基于relationship增加数据
1 from my_M2M import Girl,Boy,Hotel,engine 2 3 # 创建连接 4 from sqlalchemy.orm import sessionmaker 5 # 创建数据表操作对象 sessionmaker 6 DB_session = sessionmaker(engine) 7 db_session = DB_session() 8 9 # 1.通过Boy添加Girl和Hotel数据 10 boy = Boy(name="DragonFire") 11 boy.girl2boy = [Girl(name="赵丽颖"),Girl(name="Angelababy")] 12 db_session.add(boy) 13 db_session.commit() 14 15 # 2.通过Girl添加Boy和Hotel数据 16 girl = Girl(name="珊珊") 17 girl.boys = [Boy(name="Dragon")] 18 db_session.add(girl) 19 db_session.commit()
4.3.基于relationship查询数据
1 from my_M2M import Girl,Boy,Hotel,engine 2 3 # 创建连接 4 from sqlalchemy.orm import sessionmaker 5 # 创建数据表操作对象 sessionmaker 6 DB_session = sessionmaker(engine) 7 db_session = DB_session() 8 9 # 1.通过Boy查询约会过的所有Girl 10 hotel = db_session.query(Boy).all() 11 for row in hotel: 12 for row2 in row.girl2boy: 13 print(row.name,row2.name) 14 15 # 2.通过Girl查询约会过的所有Boy 16 hotel = db_session.query(Girl).all() 17 for row in hotel: 18 for row2 in row.boys: 19 print(row.name,row2.name)
Memcached
Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载。它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高动态、数据库驱动网站的速度。Memcached基于一个存储键/值对的hashmap。其守护进程(daemon )是用C写的,但是客户端可以用任何语言来编写,并通过memcached协议与守护进程通信。
Memcached安装和基本使用
Memcached安装:
|
1
2
3
4
5
6
7
8
|
wget http://memcached.org/latesttar -zxvf memcached-1.x.x.tar.gzcd memcached-1.x.x./configure && make && make test && sudo make installPS:依赖libevent yum install libevent-devel apt-get install libevent-dev |
启动Memcached
|
1
2
3
4
5
6
7
8
9
10
|
memcached -d -m 10 -u root -l 10.211.55.4 -p 12000 -c 256 -P /tmp/memcached.pid参数说明: -d 是启动一个守护进程 -m 是分配给Memcache使用的内存数量,单位是MB -u 是运行Memcache的用户 -l 是监听的服务器IP地址 -p 是设置Memcache监听的端口,最好是1024以上的端口 -c 选项是最大运行的并发连接数,默认是1024,按照你服务器的负载量来设定 -P 是设置保存Memcache的pid文件 |
Memcached命令
|
1
2
3
|
存储命令: set/add/replace/append/prepend/cas 获取命令: get/gets其他命令: delete/stats.. |
Python操作Memcached
安装API
|
1
2
|
python操作Memcached使用Python-memcached模块下载安装:https://pypi.python.org/pypi/python-memcached |
1、第一次操作
|
1
2
3
4
5
6
|
import memcachemc = memcache.Client(['10.211.55.4:12000'], debug=True)mc.set("foo", "bar")ret = mc.get('foo')print ret |
Ps:debug = True 表示运行出现错误时,现实错误信息,上线后移除该参数。
2、天生支持集群
python-memcached模块原生支持集群操作,其原理是在内存维护一个主机列表,且集群中主机的权重值和主机在列表中重复出现的次数成正比
|
1
2
3
4
5
6
7
|
主机 权重 1.1.1.1 1 1.1.1.2 2 1.1.1.3 1那么在内存中主机列表为: host_list = ["1.1.1.1", "1.1.1.2", "1.1.1.2", "1.1.1.3", ] |
如果用户根据如果要在内存中创建一个键值对(如:k1 = "v1"),那么要执行一下步骤:
- 根据算法将 k1 转换成一个数字
- 将数字和主机列表长度求余数,得到一个值 N( 0 <= N < 列表长度 )
- 在主机列表中根据 第2步得到的值为索引获取主机,例如:host_list[N]
- 连接 将第3步中获取的主机,将 k1 = "v1" 放置在该服务器的内存中
代码实现如下:
|
1
2
3
|
mc = memcache.Client([('1.1.1.1:12000', 1), ('1.1.1.2:12000', 2), ('1.1.1.3:12000', 1)], debug=True)mc.set('k1', 'v1') |
3、add
添加一条键值对,如果已经存在的 key,重复执行add操作异常
|
1
2
3
4
5
6
7
|
#!/usr/bin/env python# -*- coding:utf-8 -*-import memcachemc = memcache.Client(['10.211.55.4:12000'], debug=True)mc.add('k1', 'v1')# mc.add('k1', 'v2') # 报错,对已经存在的key重复添加,失败!!! |
4、replace
replace 修改某个key的值,如果key不存在,则异常
|
1
2
3
4
5
6
7
|
#!/usr/bin/env python# -*- coding:utf-8 -*-import memcachemc = memcache.Client(['10.211.55.4:12000'], debug=True)# 如果memcache中存在kkkk,则替换成功,否则一场mc.replace('kkkk','999') |
5、set 和 set_multi
set 设置一个键值对,如果key不存在,则创建,如果key存在,则修改
set_multi 设置多个键值对,如果key不存在,则创建,如果key存在,则修改
|
1
2
3
4
5
6
7
8
9
|
#!/usr/bin/env python# -*- coding:utf-8 -*-import memcachemc = memcache.Client(['10.211.55.4:12000'], debug=True)mc.set('key0', 'wupeiqi')mc.set_multi({'key1': 'val1', 'key2': 'val2'}) |
6、delete 和 delete_multi
delete 在Memcached中删除指定的一个键值对
delete_multi 在Memcached中删除指定的多个键值对
|
1
2
3
4
5
6
7
8
|
#!/usr/bin/env python# -*- coding:utf-8 -*-import memcachemc = memcache.Client(['10.211.55.4:12000'], debug=True)mc.delete('key0')mc.delete_multi(['key1', 'key2']) |
7、get 和 get_multi
get 获取一个键值对
get_multi 获取多一个键值对
|
1
2
3
4
5
6
7
8
|
#!/usr/bin/env python# -*- coding:utf-8 -*-import memcachemc = memcache.Client(['10.211.55.4:12000'], debug=True)val = mc.get('key0')item_dict = mc.get_multi(["key1", "key2", "key3"]) |
8、append 和 prepend
append 修改指定key的值,在该值 后面 追加内容
prepend 修改指定key的值,在该值 前面 插入内容
|
1
2
3
4
5
6
7
8
9
10
11
12
|
#!/usr/bin/env python# -*- coding:utf-8 -*-import memcachemc = memcache.Client(['10.211.55.4:12000'], debug=True)# k1 = "v1"mc.append('k1', 'after')# k1 = "v1after"mc.prepend('k1', 'before')# k1 = "beforev1after" |
9、decr 和 incr
incr 自增,将Memcached中的某一个值增加 N ( N默认为1 )
decr 自减,将Memcached中的某一个值减少 N ( N默认为1 )
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
#!/usr/bin/env python# -*- coding:utf-8 -*-import memcachemc = memcache.Client(['10.211.55.4:12000'], debug=True)mc.set('k1', '777')mc.incr('k1')# k1 = 778mc.incr('k1', 10)# k1 = 788mc.decr('k1')# k1 = 787mc.decr('k1', 10)# k1 = 777 |
10、gets 和 cas
如商城商品剩余个数,假设改值保存在memcache中,product_count = 900
A用户刷新页面从memcache中读取到product_count = 900
B用户刷新页面从memcache中读取到product_count = 900
如果A、B用户均购买商品
A用户修改商品剩余个数 product_count=899
B用户修改商品剩余个数 product_count=899
如此一来缓存内的数据便不在正确,两个用户购买商品后,商品剩余还是 899
如果使用python的set和get来操作以上过程,那么程序就会如上述所示情况!
如果想要避免此情况的发生,只要使用 gets 和 cas 即可,如:
|
1
2
3
4
5
6
7
8
9
|
#!/usr/bin/env python# -*- coding:utf-8 -*-import memcachemc = memcache.Client(['10.211.55.4:12000'], debug=True, cache_cas=True)v = mc.gets('product_count')# ...# 如果有人在gets之后和cas之前修改了product_count,那么,下面的设置将会执行失败,剖出异常,从而避免非正常数据的产生mc.cas('product_count', "899") |
Ps:本质上每次执行gets时,会从memcache中获取一个自增的数字,通过cas去修改gets的值时,会携带之前获取的自增值和memcache中的自增值进行比较,如果相等,则可以提交,如果不想等,那表示在gets和cas执行之间,又有其他人执行了gets(获取了缓冲的指定值), 如此一来有可能出现非正常数据,则不允许修改。
Redis
redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。
1. 使用Redis有哪些好处? (1) 速度快,因为数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1) (2) 支持丰富数据类型,支持string,list,set,sorted set,hash (3) 支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行 (4) 丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除 2. redis相比memcached有哪些优势? (1) memcached所有的值均是简单的字符串,redis作为其替代者,支持更为丰富的数据类型 (2) redis的速度比memcached快很多 (3) redis可以持久化其数据 3. redis常见性能问题和解决方案: (1) Master最好不要做任何持久化工作,如RDB内存快照和AOF日志文件 (2) 如果数据比较重要,某个Slave开启AOF备份数据,策略设置为每秒同步一次 (3) 为了主从复制的速度和连接的稳定性,Master和Slave最好在同一个局域网内 (4) 尽量避免在压力很大的主库上增加从库 (5) 主从复制不要用图状结构,用单向链表结构更为稳定,即:Master <- Slave1 <- Slave2 <- Slave3... 这样的结构方便解决单点故障问题,实现Slave对Master的替换。如果Master挂了,可以立刻启用Slave1做Master,其他不变。 4. MySQL里有2000w数据,redis中只存20w的数据,如何保证redis中的数据都是热点数据 相关知识:redis 内存数据集大小上升到一定大小的时候,就会施行数据淘汰策略。redis 提供 6种数据淘汰策略: voltile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰 volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰 volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰 allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰 allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰 no-enviction(驱逐):禁止驱逐数据 5. Memcache与Redis的区别都有哪些? 1)、存储方式 Memecache把数据全部存在内存之中,断电后会挂掉,数据不能超过内存大小。 Redis有部份存在硬盘上,这样能保证数据的持久性。 2)、数据支持类型 Memcache对数据类型支持相对简单。 Redis有复杂的数据类型。 3),value大小 redis最大可以达到1GB,而memcache只有1MB 6. Redis 常见的性能问题都有哪些?如何解决? 1).Master写内存快照,save命令调度rdbSave函数,会阻塞主线程的工作,当快照比较大时对性能影响是非常大的,会间断性暂停服务,所以Master最好不要写内存快照。 2).Master AOF持久化,如果不重写AOF文件,这个持久化方式对性能的影响是最小的,但是AOF文件会不断增大,AOF文件过大会影响Master重启的恢复速度。Master最好不要做任何持久化工作,包括内存快照和AOF日志文件,特别是不要启用内存快照做持久化,如果数据比较关键,某个Slave开启AOF备份数据,策略为每秒同步一次。 3).Master调用BGREWRITEAOF重写AOF文件,AOF在重写的时候会占大量的CPU和内存资源,导致服务load过高,出现短暂服务暂停现象。 4). Redis主从复制的性能问题,为了主从复制的速度和连接的稳定性,Slave和Master最好在同一个局域网内 7, redis 最适合的场景 Redis最适合所有数据in-momory的场景,虽然Redis也提供持久化功能,但实际更多的是一个disk-backed的功能,跟传统意义上的持久化有比较大的差别,那么可能大家就会有疑问,似乎Redis更像一个加强版的Memcached,那么何时使用Memcached,何时使用Redis呢? 如果简单地比较Redis与Memcached的区别,大多数都会得到以下观点: 1 、Redis不仅仅支持简单的k/v类型的数据,同时还提供list,set,zset,hash等数据结构的存储。 2 、Redis支持数据的备份,即master-slave模式的数据备份。 3 、Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用。 (1)、会话缓存(Session Cache) 最常用的一种使用Redis的情景是会话缓存(session cache)。用Redis缓存会话比其他存储(如Memcached)的优势在于:Redis提供持久化。当维护一个不是严格要求一致性的缓存时,如果用户的购物车信息全部丢失,大部分人都会不高兴的,现在,他们还会这样吗? 幸运的是,随着 Redis 这些年的改进,很容易找到怎么恰当的使用Redis来缓存会话的文档。甚至广为人知的商业平台Magento也提供Redis的插件。 (2)、全页缓存(FPC) 除基本的会话token之外,Redis还提供很简便的FPC平台。回到一致性问题,即使重启了Redis实例,因为有磁盘的持久化,用户也不会看到页面加载速度的下降,这是一个极大改进,类似PHP本地FPC。 再次以Magento为例,Magento提供一个插件来使用Redis作为全页缓存后端。 此外,对WordPress的用户来说,Pantheon有一个非常好的插件 wp-redis,这个插件能帮助你以最快速度加载你曾浏览过的页面。 (3)、队列 Reids在内存存储引擎领域的一大优点是提供 list 和 set 操作,这使得Redis能作为一个很好的消息队列平台来使用。Redis作为队列使用的操作,就类似于本地程序语言(如Python)对 list 的 push/pop 操作。 如果你快速的在Google中搜索“Redis queues”,你马上就能找到大量的开源项目,这些项目的目的就是利用Redis创建非常好的后端工具,以满足各种队列需求。例如,Celery有一个后台就是使用Redis作为broker,你可以从这里去查看。 (4),排行榜/计数器 Redis在内存中对数字进行递增或递减的操作实现的非常好。集合(Set)和有序集合(Sorted Set)也使得我们在执行这些操作的时候变的非常简单,Redis只是正好提供了这两种数据结构。所以,我们要从排序集合中获取到排名最靠前的10个用户–我们称之为“user_scores”,我们只需要像下面一样执行即可: 当然,这是假定你是根据你用户的分数做递增的排序。如果你想返回用户及用户的分数,你需要这样执行: ZRANGE user_scores 0 10 WITHSCORES Agora Games就是一个很好的例子,用Ruby实现的,它的排行榜就是使用Redis来存储数据的,你可以在这里看到。 (5)、发布/订阅 最后(但肯定不是最不重要的)是Redis的发布/订阅功能。发布/订阅的使用场景确实非常多。我已看见人们在社交网络连接中使用,还可作为基于发布/订阅的脚本触发器,甚至用Redis的发布/订阅功能来建立聊天系统!(不,这是真的,你可以去核实)。 Redis提供的所有特性中,我感觉这个是喜欢的人最少的一个,虽然它为用户提供如果此多功能。
一、Redis安装和基本使用
|
1
2
3
4
|
wget http://download.redis.io/releases/redis-3.0.6.tar.gztar xzf redis-3.0.6.tar.gzcd redis-3.0.6make |
启动服务端
|
1
|
src/redis-server |
启动客户端
|
1
2
3
4
5
|
src/redis-cliredis> set foo barOKredis> get foo"bar" |
二、Python操作Redis
|
1
2
3
4
5
6
7
|
sudo pip install redisorsudo easy_install redisor源码安装详见:https://github.com/WoLpH/redis-py |
API使用
redis-py 的API的使用可以分类为:
- 连接方式
- 连接池
- 操作
- String 操作
- Hash 操作
- List 操作
- Set 操作
- Sort Set 操作
- 管道
- 发布订阅
1、操作模式
redis-py提供两个类Redis和StrictRedis用于实现Redis的命令,StrictRedis用于实现大部分官方的命令,并使用官方的语法和命令,Redis是StrictRedis的子类,用于向后兼容旧版本的redis-py。
|
1
2
3
4
5
6
7
8
|
#!/usr/bin/env python# -*- coding:utf-8 -*-import redisr = redis.Redis(host='10.211.55.4', port=6379)r.set('foo', 'Bar')print r.get('foo') |
2、连接池
redis-py使用connection pool来管理对一个redis server的所有连接,避免每次建立、释放连接的开销。默认,每个Redis实例都会维护一个自己的连接池。可以直接建立一个连接池,然后作为参数Redis,这样就可以实现多个Redis实例共享一个连接池。
|
1
2
3
4
5
6
7
8
9
10
|
#!/usr/bin/env python# -*- coding:utf-8 -*-import redispool = redis.ConnectionPool(host='10.211.55.4', port=6379)r = redis.Redis(connection_pool=pool)r.set('foo', 'Bar')print r.get('foo') |
3、操作
String操作,redis中的String在在内存中按照一个name对应一个value来存储。如图:

set(name, value, ex=None, px=None, nx=False, xx=False)
123456在Redis中设置值,默认,不存在则创建,存在则修改参数:ex,过期时间(秒)px,过期时间(毫秒)nx,如果设置为True,则只有name不存在时,当前set操作才执行xx,如果设置为True,则只有name存在时,岗前set操作才执行setnx(name, value)
1设置值,只有name不存在时,执行设置操作(添加)setex(name, value, time)
123# 设置值# 参数:# time,过期时间(数字秒 或 timedelta对象)psetex(name, time_ms, value)
123# 设置值# 参数:# time_ms,过期时间(数字毫秒 或 timedelta对象)mset(*args, **kwargs)
12345批量设置值如:mset(k1='v1', k2='v2')或mget({'k1':'v1','k2':'v2'})get(name)
1获取值mget(keys, *args)
12345批量获取如:mget('ylr','wupeiqi')或r.mget(['ylr','wupeiqi'])getset(name, value)
1设置新值并获取原来的值getrange(key, start, end)
123456# 获取子序列(根据字节获取,非字符)# 参数:# name,Redis 的 name# start,起始位置(字节)# end,结束位置(字节)# 如: "武沛齐" ,0-3表示 "武"setrange(name, offset, value)
1234# 修改字符串内容,从指定字符串索引开始向后替换(新值太长时,则向后添加)# 参数:# offset,字符串的索引,字节(一个汉字三个字节)# value,要设置的值setbit(name, offset, value)
123456789101112131415161718192021222324252627# 对name对应值的二进制表示的位进行操作# 参数:# name,redis的name# offset,位的索引(将值变换成二进制后再进行索引)# value,值只能是 1 或 0# 注:如果在Redis中有一个对应: n1 = "foo",那么字符串foo的二进制表示为:011001100110111101101111所以,如果执行 setbit('n1',7,1),则就会将第7位设置为1,那么最终二进制则变成011001110110111101101111,即:"goo"# 扩展,转换二进制表示:# source = "武沛齐"source="foo"foriinsource:num=ord(i)bin(num).replace('b','')特别的,如果source是汉字"武沛齐"怎么办?答:对于utf-8,每一个汉字占3个字节,那么"武沛齐"则有9个字节对于汉字,for循环时候会按照 字节 迭代,那么在迭代时,将每一个字节转换 十进制数,然后再将十进制数转换成二进制111001101010110110100110111001101011001010011011111010011011110110010000------------------------------------------------------------------------------------武 沛 齐getbit(name, offset)
1# 获取name对应的值的二进制表示中的某位的值 (0或1)bitcount(key, start=None, end=None)
12345# 获取name对应的值的二进制表示中 1 的个数# 参数:# key,Redis的name# start,位起始位置# end,位结束位置bitop(operation, dest, *keys)
12345678910# 获取多个值,并将值做位运算,将最后的结果保存至新的name对应的值# 参数:# operation,AND(并) 、 OR(或) 、 NOT(非) 、 XOR(异或)# dest, 新的Redis的name# *keys,要查找的Redis的name# 如:bitop("AND",'new_name','n1','n2','n3')# 获取Redis中n1,n2,n3对应的值,然后讲所有的值做位运算(求并集),然后将结果保存 new_name 对应的值中strlen(name)
1# 返回name对应值的字节长度(一个汉字3个字节)incr(self, name, amount=1)
1234567# 自增 name对应的值,当name不存在时,则创建name=amount,否则,则自增。# 参数:# name,Redis的name# amount,自增数(必须是整数)# 注:同incrbyincrbyfloat(self, name, amount=1.0)
12345# 自增 name对应的值,当name不存在时,则创建name=amount,否则,则自增。# 参数:# name,Redis的name# amount,自增数(浮点型)decr(self, name, amount=1)
12345# 自减 name对应的值,当name不存在时,则创建name=amount,否则,则自减。# 参数:# name,Redis的name# amount,自减数(整数)append(key, value)
12345# 在redis name对应的值后面追加内容# 参数:key, redis的namevalue, 要追加的字符串
Hash操作,redis中Hash在内存中的存储格式如下图:

hset(name, key, value)
123456789# name对应的hash中设置一个键值对(不存在,则创建;否则,修改)# 参数:# name,redis的name# key,name对应的hash中的key# value,name对应的hash中的value# 注:# hsetnx(name, key, value),当name对应的hash中不存在当前key时则创建(相当于添加)hmset(name, mapping)
12345678# 在name对应的hash中批量设置键值对# 参数:# name,redis的name# mapping,字典,如:{'k1':'v1', 'k2': 'v2'}# 如:# r.hmset('xx', {'k1':'v1', 'k2': 'v2'})hget(name,key)
1# 在name对应的hash中获取根据key获取valuehmget(name, keys, *args)
1234567891011# 在name对应的hash中获取多个key的值# 参数:# name,reids对应的name# keys,要获取key集合,如:['k1', 'k2', 'k3']# *args,要获取的key,如:k1,k2,k3# 如:# r.mget('xx', ['k1', 'k2'])# 或# print r.hmget('xx', 'k1', 'k2')hgetall(name)
1获取name对应hash的所有键值hlen(name)
1# 获取name对应的hash中键值对的个数hkeys(name)
1# 获取name对应的hash中所有的key的值hvals(name)
1# 获取name对应的hash中所有的value的值hexists(name, key)
1# 检查name对应的hash是否存在当前传入的keyhdel(name,*keys)
1# 将name对应的hash中指定key的键值对删除hincrby(name, key, amount=1)
12345# 自增name对应的hash中的指定key的值,不存在则创建key=amount# 参数:# name,redis中的name# key, hash对应的key# amount,自增数(整数)hincrbyfloat(name, key, amount=1.0)
12345678# 自增name对应的hash中的指定key的值,不存在则创建key=amount# 参数:# name,redis中的name# key, hash对应的key# amount,自增数(浮点数)# 自增name对应的hash中的指定key的值,不存在则创建key=amounthscan(name, cursor=0, match=None, count=None)
12345678910111213# 增量式迭代获取,对于数据大的数据非常有用,hscan可以实现分片的获取数据,并非一次性将数据全部获取完,从而放置内存被撑爆# 参数:# name,redis的name# cursor,游标(基于游标分批取获取数据)# match,匹配指定key,默认None 表示所有的key# count,每次分片最少获取个数,默认None表示采用Redis的默认分片个数# 如:# 第一次:cursor1, data1 = r.hscan('xx', cursor=0, match=None, count=None)# 第二次:cursor2, data1 = r.hscan('xx', cursor=cursor1, match=None, count=None)# ...# 直到返回值cursor的值为0时,表示数据已经通过分片获取完毕hscan_iter(name, match=None, count=None)
123456789# 利用yield封装hscan创建生成器,实现分批去redis中获取数据# 参数:# match,匹配指定key,默认None 表示所有的key# count,每次分片最少获取个数,默认None表示采用Redis的默认分片个数# 如:# for item in r.hscan_iter('xx'):# print item
List操作,redis中的List在在内存中按照一个name对应一个List来存储。如图:

lpush(name,values)
12345678# 在name对应的list中添加元素,每个新的元素都添加到列表的最左边# 如:# r.lpush('oo', 11,22,33)# 保存顺序为: 33,22,11# 扩展:# rpush(name, values) 表示从右向左操作lpushx(name,value)
1234# 在name对应的list中添加元素,只有name已经存在时,值添加到列表的最左边# 更多:# rpushx(name, value) 表示从右向左操作llen(name)
1# name对应的list元素的个数linsert(name, where, refvalue, value))
1234567# 在name对应的列表的某一个值前或后插入一个新值# 参数:# name,redis的name# where,BEFORE或AFTER# refvalue,标杆值,即:在它前后插入数据# value,要插入的数据r.lset(name, index, value)
123456# 对name对应的list中的某一个索引位置重新赋值# 参数:# name,redis的name# index,list的索引位置# value,要设置的值r.lrem(name, value, num)
12345678# 在name对应的list中删除指定的值# 参数:# name,redis的name# value,要删除的值# num, num=0,删除列表中所有的指定值;# num=2,从前到后,删除2个;# num=-2,从后向前,删除2个lpop(name)
1234# 在name对应的列表的左侧获取第一个元素并在列表中移除,返回值则是第一个元素# 更多:# rpop(name) 表示从右向左操作lindex(name, index)
1在name对应的列表中根据索引获取列表元素lrange(name, start, end)
12345# 在name对应的列表分片获取数据# 参数:# name,redis的name# start,索引的起始位置# end,索引结束位置ltrim(name, start, end)
12345# 在name对应的列表中移除没有在start-end索引之间的值# 参数:# name,redis的name# start,索引的起始位置# end,索引结束位置rpoplpush(src, dst)
1234# 从一个列表取出最右边的元素,同时将其添加至另一个列表的最左边# 参数:# src,要取数据的列表的name# dst,要添加数据的列表的nameblpop(keys, timeout)
12345678# 将多个列表排列,按照从左到右去pop对应列表的元素# 参数:# keys,redis的name的集合# timeout,超时时间,当元素所有列表的元素获取完之后,阻塞等待列表内有数据的时间(秒), 0 表示永远阻塞# 更多:# r.brpop(keys, timeout),从右向左获取数据brpoplpush(src, dst, timeout=0)
123456# 从一个列表的右侧移除一个元素并将其添加到另一个列表的左侧# 参数:# src,取出并要移除元素的列表对应的name# dst,要插入元素的列表对应的name# timeout,当src对应的列表中没有数据时,阻塞等待其有数据的超时时间(秒),0 表示永远阻塞自定义增量迭代
123456789101112131415161718# 由于redis类库中没有提供对列表元素的增量迭代,如果想要循环name对应的列表的所有元素,那么就需要:# 1、获取name对应的所有列表# 2、循环列表# 但是,如果列表非常大,那么就有可能在第一步时就将程序的内容撑爆,所有有必要自定义一个增量迭代的功能:deflist_iter(name):"""自定义redis列表增量迭代:param name: redis中的name,即:迭代name对应的列表:return: yield 返回 列表元素"""list_count=r.llen(name)forindexinxrange(list_count):yieldr.lindex(name, index)# 使用foriteminlist_iter('pp'):itemSet操作,Set集合就是不允许重复的列表
sadd(name,values)
1# name对应的集合中添加元素scard(name)
1获取name对应的集合中元素个数sdiff(keys, *args)
1在第一个name对应的集合中且不在其他name对应的集合的元素集合sdiffstore(dest, keys, *args)
1# 获取第一个name对应的集合中且不在其他name对应的集合,再将其新加入到dest对应的集合中sinter(keys, *args)
1# 获取多一个name对应集合的并集sinterstore(dest, keys, *args)
1# 获取多一个name对应集合的并集,再讲其加入到dest对应的集合中sismember(name, value)
1# 检查value是否是name对应的集合的成员smembers(name)
1# 获取name对应的集合的所有成员smove(src, dst, value)
1# 将某个成员从一个集合中移动到另外一个集合spop(name)
1# 从集合的右侧(尾部)移除一个成员,并将其返回srandmember(name, numbers)
1# 从name对应的集合中随机获取 numbers 个元素srem(name, values)
1# 在name对应的集合中删除某些值sunion(keys, *args)
1# 获取多一个name对应的集合的并集sunionstore(dest,keys, *args)
1# 获取多一个name对应的集合的并集,并将结果保存到dest对应的集合中sscan(name, cursor=0, match=None, count=None)
sscan_iter(name, match=None, count=None)
1# 同字符串的操作,用于增量迭代分批获取元素,避免内存消耗太大
有序集合,在集合的基础上,为每元素排序;元素的排序需要根据另外一个值来进行比较,所以,对于有序集合,每一个元素有两个值,即:值和分数,分数专门用来做排序。
zadd(name, *args, **kwargs)
12345# 在name对应的有序集合中添加元素# 如:# zadd('zz', 'n1', 1, 'n2', 2)# 或# zadd('zz', n1=11, n2=22)zcard(name)
1# 获取name对应的有序集合元素的数量zcount(name, min, max)
1# 获取name对应的有序集合中分数 在 [min,max] 之间的个数zincrby(name, value, amount)
1# 自增name对应的有序集合的 name 对应的分数r.zrange( name, start, end, desc=False, withscores=False, score_cast_func=float)
123456789101112131415161718# 按照索引范围获取name对应的有序集合的元素# 参数:# name,redis的name# start,有序集合索引起始位置(非分数)# end,有序集合索引结束位置(非分数)# desc,排序规则,默认按照分数从小到大排序# withscores,是否获取元素的分数,默认只获取元素的值# score_cast_func,对分数进行数据转换的函数# 更多:# 从大到小排序# zrevrange(name, start, end, withscores=False, score_cast_func=float)# 按照分数范围获取name对应的有序集合的元素# zrangebyscore(name, min, max, start=None, num=None, withscores=False, score_cast_func=float)# 从大到小排序# zrevrangebyscore(name, max, min, start=None, num=None, withscores=False, score_cast_func=float)zrank(name, value)
1234# 获取某个值在 name对应的有序集合中的排行(从 0 开始)# 更多:# zrevrank(name, value),从大到小排序zrangebylex(name, min, max, start=None, num=None)
1234567891011121314151617# 当有序集合的所有成员都具有相同的分值时,有序集合的元素会根据成员的 值 (lexicographical ordering)来进行排序,而这个命令则可以返回给定的有序集合键 key 中, 元素的值介于 min 和 max 之间的成员# 对集合中的每个成员进行逐个字节的对比(byte-by-byte compare), 并按照从低到高的顺序, 返回排序后的集合成员。 如果两个字符串有一部分内容是相同的话, 那么命令会认为较长的字符串比较短的字符串要大# 参数:# name,redis的name# min,左区间(值)。 + 表示正无限; - 表示负无限; ( 表示开区间; [ 则表示闭区间# min,右区间(值)# start,对结果进行分片处理,索引位置# num,对结果进行分片处理,索引后面的num个元素# 如:# ZADD myzset 0 aa 0 ba 0 ca 0 da 0 ea 0 fa 0 ga# r.zrangebylex('myzset', "-", "[ca") 结果为:['aa', 'ba', 'ca']# 更多:# 从大到小排序# zrevrangebylex(name, max, min, start=None, num=None)zrem(name, values)
123# 删除name对应的有序集合中值是values的成员# 如:zrem('zz', ['s1', 's2'])zremrangebyrank(name, min, max)
1# 根据排行范围删除zremrangebyscore(name, min, max)
1# 根据分数范围删除zremrangebylex(name, min, max)
1# 根据值返回删除zscore(name, value)
1# 获取name对应有序集合中 value 对应的分数zinterstore(dest, keys, aggregate=None)
12# 获取两个有序集合的交集,如果遇到相同值不同分数,则按照aggregate进行操作# aggregate的值为: SUM MIN MAXzunionstore(dest, keys, aggregate=None)
12# 获取两个有序集合的并集,如果遇到相同值不同分数,则按照aggregate进行操作# aggregate的值为: SUM MIN MAXzscan(name, cursor=0, match=None, count=None, score_cast_func=float)
zscan_iter(name, match=None, count=None,score_cast_func=float)
1# 同字符串相似,相较于字符串新增score_cast_func,用来对分数进行操作
其他常用操作
delete(*names)
1# 根据删除redis中的任意数据类型exists(name)
1# 检测redis的name是否存在keys(pattern='*')
1234567# 根据模型获取redis的name# 更多:# KEYS * 匹配数据库中所有 key 。# KEYS h?llo 匹配 hello , hallo 和 hxllo 等。# KEYS h*llo 匹配 hllo 和 heeeeello 等。# KEYS h[ae]llo 匹配 hello 和 hallo ,但不匹配 hilloexpire(name ,time)
1# 为某个redis的某个name设置超时时间rename(src, dst)
1# 对redis的name重命名为move(name, db))
1# 将redis的某个值移动到指定的db下randomkey()
1# 随机获取一个redis的name(不删除)type(name)
1# 获取name对应值的类型scan(cursor=0, match=None, count=None)
scan_iter(match=None, count=None)
1# 同字符串操作,用于增量迭代获取key
4、管道
redis-py默认在执行每次请求都会创建(连接池申请连接)和断开(归还连接池)一次连接操作,如果想要在一次请求中指定多个命令,则可以使用pipline实现一次请求指定多个命令,并且默认情况下一次pipline 是原子性操作。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
#!/usr/bin/env python# -*- coding:utf-8 -*-import redispool = redis.ConnectionPool(host='10.211.55.4', port=6379)r = redis.Redis(connection_pool=pool)# pipe = r.pipeline(transaction=False)pipe = r.pipeline(transaction=True)pipe.set('name', 'alex')pipe.set('role', 'sb')pipe.execute() |
5、发布订阅

发布者:服务器
订阅者:Dashboad和数据处理
Demo如下:
#!/usr/bin/env python # -*- coding:utf-8 -*- import redis class RedisHelper: def __init__(self): self.__conn = redis.Redis(host='10.211.55.4') self.chan_sub = 'fm104.5' self.chan_pub = 'fm104.5' def public(self, msg): self.__conn.publish(self.chan_pub, msg) return True def subscribe(self): pub = self.__conn.pubsub() pub.subscribe(self.chan_sub) pub.parse_response() return pub
订阅者:
|
1
2
3
4
5
6
7
8
9
10
11
|
#!/usr/bin/env python# -*- coding:utf-8 -*-from monitor.RedisHelper import RedisHelperobj = RedisHelper()redis_sub = obj.subscribe()while True: msg= redis_sub.parse_response() print msg |
发布者:
|
1
2
3
4
5
6
7
|
#!/usr/bin/env python# -*- coding:utf-8 -*-from monitor.RedisHelper import RedisHelperobj = RedisHelper()obj.public('hello') |
更多参见:https://github.com/andymccurdy/redis-py/
http://doc.redisfans.com/
RabbitMQ
RabbitMQ是一个在AMQP基础上完整的,可复用的企业消息系统。他遵循Mozilla Public License开源协议。
MQ全称为Message Queue, 消息队列(MQ)是一种应用程序对应用程序的通信方法。应用程序通过读写出入队列的消息(针对应用程序的数据)来通信,而无需专用连接来链接它们。消 息传递指的是程序之间通过在消息中发送数据进行通信,而不是通过直接调用彼此来通信,直接调用通常是用于诸如远程过程调用的技术。排队指的是应用程序通过 队列来通信。队列的使用除去了接收和发送应用程序同时执行的要求。
RabbitMQ安装
|
1
2
3
4
5
6
7
8
|
安装配置epel源 $ rpm -ivh http://dl.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpm安装erlang $ yum -y install erlang安装RabbitMQ $ yum -y install rabbitmq-server |
注意:service rabbitmq-server start/stop
安装API
|
1
2
3
4
5
6
7
|
pip install pikaoreasy_install pikaor源码https://pypi.python.org/pypi/pika |
使用API操作RabbitMQ
基于Queue实现生产者消费者模型
#!/usr/bin/env python # -*- coding:utf-8 -*- import Queue import threading message = Queue.Queue(10) def producer(i): while True: message.put(i) def consumer(i): while True: msg = message.get() for i in range(12): t = threading.Thread(target=producer, args=(i,)) t.start() for i in range(10): t = threading.Thread(target=consumer, args=(i,)) t.start()
对于RabbitMQ来说,生产和消费不再针对内存里的一个Queue对象,而是某台服务器上的RabbitMQ Server实现的消息队列。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
#!/usr/bin/env pythonimport pika# ######################### 生产者 #########################connection = pika.BlockingConnection(pika.ConnectionParameters( host='localhost'))channel = connection.channel()channel.queue_declare(queue='hello')channel.basic_publish(exchange='', routing_key='hello', body='Hello World!')print(" [x] Sent 'Hello World!'")connection.close() |
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
#!/usr/bin/env pythonimport pika# ########################## 消费者 ##########################connection = pika.BlockingConnection(pika.ConnectionParameters( host='localhost'))channel = connection.channel()channel.queue_declare(queue='hello')def callback(ch, method, properties, body): print(" [x] Received %r" % body)channel.basic_consume(callback, queue='hello', no_ack=True)print(' [*] Waiting for messages. To exit press CTRL+C')channel.start_consuming() |
1、acknowledgment 消息不丢失
no-ack = False,如果消费者遇到情况(its channel is closed, connection is closed, or TCP connection is lost)挂掉了,那么,RabbitMQ会重新将该任务添加到队列中。
import pika connection = pika.BlockingConnection(pika.ConnectionParameters( host='10.211.55.4')) channel = connection.channel() channel.queue_declare(queue='hello') def callback(ch, method, properties, body): print(" [x] Received %r" % body) import time time.sleep(10) print 'ok' ch.basic_ack(delivery_tag = method.delivery_tag) channel.basic_consume(callback, queue='hello', no_ack=False) print(' [*] Waiting for messages. To exit press CTRL+C') channel.start_consuming()
2、durable 消息不丢失
#!/usr/bin/env python import pika connection = pika.BlockingConnection(pika.ConnectionParameters(host='10.211.55.4')) channel = connection.channel() # make message persistent channel.queue_declare(queue='hello', durable=True) channel.basic_publish(exchange='', routing_key='hello', body='Hello World!', properties=pika.BasicProperties( delivery_mode=2, # make message persistent )) print(" [x] Sent 'Hello World!'") connection.close()
#!/usr/bin/env python # -*- coding:utf-8 -*- import pika connection = pika.BlockingConnection(pika.ConnectionParameters(host='10.211.55.4')) channel = connection.channel() # make message persistent channel.queue_declare(queue='hello', durable=True) def callback(ch, method, properties, body): print(" [x] Received %r" % body) import time time.sleep(10) print 'ok' ch.basic_ack(delivery_tag = method.delivery_tag) channel.basic_consume(callback, queue='hello', no_ack=False) print(' [*] Waiting for messages. To exit press CTRL+C') channel.start_consuming()
3、消息获取顺序
默认消息队列里的数据是按照顺序被消费者拿走,例如:消费者1 去队列中获取 奇数 序列的任务,消费者1去队列中获取 偶数 序列的任务。
channel.basic_qos(prefetch_count=1) 表示谁来谁取,不再按照奇偶数排列
#!/usr/bin/env python # -*- coding:utf-8 -*- import pika connection = pika.BlockingConnection(pika.ConnectionParameters(host='10.211.55.4')) channel = connection.channel() # make message persistent channel.queue_declare(queue='hello') def callback(ch, method, properties, body): print(" [x] Received %r" % body) import time time.sleep(10) print 'ok' ch.basic_ack(delivery_tag = method.delivery_tag) channel.basic_qos(prefetch_count=1) channel.basic_consume(callback, queue='hello', no_ack=False) print(' [*] Waiting for messages. To exit press CTRL+C') channel.start_consuming()
4、发布订阅

发布订阅和简单的消息队列区别在于,发布订阅会将消息发送给所有的订阅者,而消息队列中的数据被消费一次便消失。所以,RabbitMQ实现发布和订阅时,会为每一个订阅者创建一个队列,而发布者发布消息时,会将消息放置在所有相关队列中。
exchange type = fanout
#!/usr/bin/env python import pika import sys connection = pika.BlockingConnection(pika.ConnectionParameters( host='localhost')) channel = connection.channel() channel.exchange_declare(exchange='logs', type='fanout') message = ' '.join(sys.argv[1:]) or "info: Hello World!" channel.basic_publish(exchange='logs', routing_key='', body=message) print(" [x] Sent %r" % message) connection.close()
#!/usr/bin/env python import pika connection = pika.BlockingConnection(pika.ConnectionParameters( host='localhost')) channel = connection.channel() channel.exchange_declare(exchange='logs', type='fanout') result = channel.queue_declare(exclusive=True) queue_name = result.method.queue channel.queue_bind(exchange='logs', queue=queue_name) print(' [*] Waiting for logs. To exit press CTRL+C') def callback(ch, method, properties, body): print(" [x] %r" % body) channel.basic_consume(callback, queue=queue_name, no_ack=True) channel.start_consuming()
5、关键字发送

exchange type = direct
之前事例,发送消息时明确指定某个队列并向其中发送消息,RabbitMQ还支持根据关键字发送,即:队列绑定关键字,发送者将数据根据关键字发送到消息exchange,exchange根据 关键字 判定应该将数据发送至指定队列。
#!/usr/bin/env python import pika import sys connection = pika.BlockingConnection(pika.ConnectionParameters( host='localhost')) channel = connection.channel() channel.exchange_declare(exchange='direct_logs', type='direct') result = channel.queue_declare(exclusive=True) queue_name = result.method.queue severities = sys.argv[1:] if not severities: sys.stderr.write("Usage: %s [info] [warning] [error]\n" % sys.argv[0]) sys.exit(1) for severity in severities: channel.queue_bind(exchange='direct_logs', queue=queue_name, routing_key=severity) print(' [*] Waiting for logs. To exit press CTRL+C') def callback(ch, method, properties, body): print(" [x] %r:%r" % (method.routing_key, body)) channel.basic_consume(callback, queue=queue_name, no_ack=True) channel.start_consuming()
#!/usr/bin/env python import pika import sys connection = pika.BlockingConnection(pika.ConnectionParameters( host='localhost')) channel = connection.channel() channel.exchange_declare(exchange='direct_logs', type='direct') severity = sys.argv[1] if len(sys.argv) > 1 else 'info' message = ' '.join(sys.argv[2:]) or 'Hello World!' channel.basic_publish(exchange='direct_logs', routing_key=severity, body=message) print(" [x] Sent %r:%r" % (severity, message)) connection.close()
6、模糊匹配

exchange type = topic
在topic类型下,可以让队列绑定几个模糊的关键字,之后发送者将数据发送到exchange,exchange将传入”路由值“和 ”关键字“进行匹配,匹配成功,则将数据发送到指定队列。
- # 表示可以匹配 0 个 或 多个 单词
- * 表示只能匹配 一个 单词
|
1
2
3
|
发送者路由值 队列中old.boy.python old.* -- 不匹配old.boy.python old.# -- 匹配 |
#!/usr/bin/env python import pika import sys connection = pika.BlockingConnection(pika.ConnectionParameters( host='localhost')) channel = connection.channel() channel.exchange_declare(exchange='topic_logs', type='topic') result = channel.queue_declare(exclusive=True) queue_name = result.method.queue binding_keys = sys.argv[1:] if not binding_keys: sys.stderr.write("Usage: %s [binding_key]...\n" % sys.argv[0]) sys.exit(1) for binding_key in binding_keys: channel.queue_bind(exchange='topic_logs', queue=queue_name, routing_key=binding_key) print(' [*] Waiting for logs. To exit press CTRL+C') def callback(ch, method, properties, body): print(" [x] %r:%r" % (method.routing_key, body)) channel.basic_consume(callback, queue=queue_name, no_ack=True) channel.start_consuming()
#!/usr/bin/env python import pika import sys connection = pika.BlockingConnection(pika.ConnectionParameters( host='localhost')) channel = connection.channel() channel.exchange_declare(exchange='topic_logs', type='topic') routing_key = sys.argv[1] if len(sys.argv) > 1 else 'anonymous.info' message = ' '.join(sys.argv[2:]) or 'Hello World!' channel.basic_publish(exchange='topic_logs', routing_key=routing_key, body=message) print(" [x] Sent %r:%r" % (routing_key, message)) connection.close()
注意:
sudo rabbitmqctl add_user alex 123 # 设置用户为administrator角色 sudo rabbitmqctl set_user_tags alex administrator # 设置权限 sudo rabbitmqctl set_permissions -p "/" alex '.''.''.' # 然后重启rabbiMQ服务 sudo /etc/init.d/rabbitmq-server restart # 然后可以使用刚才的用户远程连接rabbitmq server了。 ------------------------------ credentials = pika.PlainCredentials("alex","123") connection = pika.BlockingConnection(pika.ConnectionParameters('192.168.14.47',credentials=credentials))
SQLAlchemy
SQLAlchemy是Python编程语言下的一款ORM框架,该框架建立在数据库API之上,使用关系对象映射进行数据库操作,简言之便是:将对象转换成SQL,然后使用数据API执行SQL并获取执行结果。

Dialect用于和数据API进行交流,根据配置文件的不同调用不同的数据库API,从而实现对数据库的操作,如:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
MySQL-Python mysql+mysqldb://<user>:<password>@<host>[:<port>]/<dbname>pymysql mysql+pymysql://<username>:<password>@<host>/<dbname>[?<options>]MySQL-Connector mysql+mysqlconnector://<user>:<password>@<host>[:<port>]/<dbname>cx_Oracle oracle+cx_oracle://user:pass@host:port/dbname[?key=value&key=value...]更多详见:http://docs.sqlalchemy.org/en/latest/dialects/index.html |
步骤一:
使用 Engine/ConnectionPooling/Dialect 进行数据库操作,Engine使用ConnectionPooling连接数据库,然后再通过Dialect执行SQL语句。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
#!/usr/bin/env python# -*- coding:utf-8 -*-from sqlalchemy import create_engineengine = create_engine("mysql+mysqldb://root:123@127.0.0.1:3306/s11", max_overflow=5)engine.execute( "INSERT INTO ts_test (a, b) VALUES ('2', 'v1')")engine.execute( "INSERT INTO ts_test (a, b) VALUES (%s, %s)", ((555, "v1"),(666, "v1"),))engine.execute( "INSERT INTO ts_test (a, b) VALUES (%(id)s, %(name)s)", id=999, name="v1")result = engine.execute('select * from ts_test')result.fetchall() |
#!/usr/bin/env python # -*- coding:utf-8 -*- from sqlalchemy import create_engine engine = create_engine("mysql+mysqldb://root:123@127.0.0.1:3306/s11", max_overflow=5) # 事务操作 with engine.begin() as conn: conn.execute("insert into table (x, y, z) values (1, 2, 3)") conn.execute("my_special_procedure(5)") conn = engine.connect() # 事务操作 with conn.begin(): conn.execute("some statement", {'x':5, 'y':10})
注:查看数据库连接:show status like 'Threads%';
步骤二:
使用 Schema Type/SQL Expression Language/Engine/ConnectionPooling/Dialect 进行数据库操作。Engine使用Schema Type创建一个特定的结构对象,之后通过SQL Expression Language将该对象转换成SQL语句,然后通过 ConnectionPooling 连接数据库,再然后通过 Dialect 执行SQL,并获取结果。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
#!/usr/bin/env python# -*- coding:utf-8 -*-from sqlalchemy import create_engine, Table, Column, Integer, String, MetaData, ForeignKeymetadata = MetaData()user = Table('user', metadata, Column('id', Integer, primary_key=True), Column('name', String(20)),)color = Table('color', metadata, Column('id', Integer, primary_key=True), Column('name', String(20)),)engine = create_engine("mysql+mysqldb://root:123@127.0.0.1:3306/s11", max_overflow=5)metadata.create_all(engine)# metadata.clear()# metadata.remove() |
#!/usr/bin/env python # -*- coding:utf-8 -*- from sqlalchemy import create_engine, Table, Column, Integer, String, MetaData, ForeignKey metadata = MetaData() user = Table('user', metadata, Column('id', Integer, primary_key=True), Column('name', String(20)), ) color = Table('color', metadata, Column('id', Integer, primary_key=True), Column('name', String(20)), ) engine = create_engine("mysql+mysqldb://root:123@127.0.0.1:3306/s11", max_overflow=5) conn = engine.connect() # 创建SQL语句,INSERT INTO "user" (id, name) VALUES (:id, :name) conn.execute(user.insert(),{'id':7,'name':'seven'}) conn.close() # sql = user.insert().values(id=123, name='wu') # conn.execute(sql) # conn.close() # sql = user.delete().where(user.c.id > 1) # sql = user.update().values(fullname=user.c.name) # sql = user.update().where(user.c.name == 'jack').values(name='ed') # sql = select([user, ]) # sql = select([user.c.id, ]) # sql = select([user.c.name, color.c.name]).where(user.c.id==color.c.id) # sql = select([user.c.name]).order_by(user.c.name) # sql = select([user]).group_by(user.c.name) # result = conn.execute(sql) # print result.fetchall() # conn.close()
更多内容详见:
http://www.jianshu.com/p/e6bba189fcbd
http://docs.sqlalchemy.org/en/latest/core/expression_api.html
注:SQLAlchemy无法修改表结构,如果需要可以使用SQLAlchemy开发者开源的另外一个软件Alembic来完成。
步骤三:
使用 ORM/Schema Type/SQL Expression Language/Engine/ConnectionPooling/Dialect 所有组件对数据进行操作。根据类创建对象,对象转换成SQL,执行SQL。
|
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
|
#!/usr/bin/env python# -*- coding:utf-8 -*-from sqlalchemy.ext.declarative import declarative_basefrom sqlalchemy import Column, Integer, Stringfrom sqlalchemy.orm import sessionmakerfrom sqlalchemy import create_engineengine = create_engine("mysql+mysqldb://root:123@127.0.0.1:3306/s11", max_overflow=5)Base = declarative_base()class User(Base): __tablename__ = 'users' id = Column(Integer, primary_key=True) name = Column(String(50))# 寻找Base的所有子类,按照子类的结构在数据库中生成对应的数据表信息# Base.metadata.create_all(engine)Session = sessionmaker(bind=engine)session = Session()# ########## 增 ########### u = User(id=2, name='sb')# session.add(u)# session.add_all([# User(id=3, name='sb'),# User(id=4, name='sb')# ])# session.commit()# ########## 删除 ########### session.query(User).filter(User.id > 2).delete()# session.commit()# ########## 修改 ########### session.query(User).filter(User.id > 2).update({'cluster_id' : 0})# session.commit()# ########## 查 ########### ret = session.query(User).filter_by(name='sb').first()# ret = session.query(User).filter_by(name='sb').all()# print ret# ret = session.query(User).filter(User.name.in_(['sb','bb'])).all()# print ret# ret = session.query(User.name.label('name_label')).all()# print ret,type(ret)# ret = session.query(User).order_by(User.id).all()# print ret# ret = session.query(User).order_by(User.id)[1:3]# print ret# session.commit() |
更多功能参见文档,猛击这里下载PDF

浙公网安备 33010602011771号