应用场景
这个可能要依据所采用的存储引擎(myisam,innerdb)来看。
读写分离
在Django框架下,数据库的读写分离的设计一般有两种方式:
- 手动实现
获取queryset的时候调用using
transaction = PrepayTransactions.objects.using('read').filter(channel='wechat', deactivated_at__is_null=True).first()
transaction.channel = 'alipy'
transaction.save(using='read')
- 自动处理
重写数据库路由,然后在settings文件里添加上该路由的具体path
class MasterSlaveDBRouter(object):
"""数据库主从读写分离路由"""
def db_for_read(self, model, **hints):
"""读数据库"""
return "slave"
def db_for_write(self, model, **hints):
"""写数据库"""
return "default"
def allow_relation(self, obj1, obj2, **hints):
"""是否运行关联操作"""
return True
然后配置读写分离(settings.py)
DATABASE_ROUTERS = [‘path_to_router.MasterSlaveDBRouter’]
主从复制
这个一般是从MySQL的高可用性来说的,都是基于MySQL Replication来实现的,模式主要有四种:
- 一主一从
- 一主多从
- 主主互备
- 双主多从
其中读业务数据量特别大的时候考虑多从架构,另外为了实现一台master出现故障时自动切换,可以采用keepalived(主主互备架构,只会监控到主节点的状态)、MMM(双主多从比较好,同时监控master和slave节点的状态)等工具来实现。
乐观锁和悲观锁
- 乐观锁
乐观的认为在自己取数据的时候不会有其他线程来更改数据,因此取数据的时候不加锁,但对数据进行update等写的操作时要加锁(这个一般通过版本号设计,CAS设计来实现) - 悲观锁
不管是读数据还是更新数据都加上锁,这个一般在数据库级别来实现,比如mysql自己的锁机制(行锁,表锁,读锁,写锁)
选择问题:这个要依据实际的应用场景来决定,一般来讲,读频繁的数据库加乐观锁,而写频繁的数据库加悲观锁。
事务及事务的隔离级别
事务的四大特性
ACID:
原子性,一致性,隔离性,持久性
事物的隔离级别
- 读未提交 — 脏读
- 读提交 — 不可重复读
- 可重复读 — 幻读
- 串行化 — 事务隔离的最高级别
可序列化的数据库锁情况:
事务在读取数据时,必须先对其加 表级共享锁 ,直到事务结束才释放 — 其他事务通过添加共享锁也可以读取数据
事务在更新数据时,必须先对其加 表级排他锁 ,直到事务结束才释放 — 其他事务不能对其进行读写操作
另外,补充几个概念:
脏读:事务A修改了某个数据但未提交,然后事务B读了这条数据(更改后),后来事务A回滚了,这就形成事务B的脏读;
不可重复读:事务A先读了某个数据,而事务B对这个数据进行了更改且提交了,这时候事务A再读这个数据时发现跟之前读的数据不一样了,这就是不可重复读;
幻读:其实跟不可重复读有点类似,只不过幻读侧重新增数据,而不可重复读则侧重更新或者删除;
网友评论