事务、并发、锁机制的实现
配置全局事务
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'mydb',
'USER':'root',
'PASSWORD':'pass',
'HOST':'127.0.0.1',
'PORT':3306,
'ATOMIC_REQUESTS': True, # 全局开启事务,绑定的是http请求响应整个过程
# (non_atomic_requests可局部实现不让事务控制)
}
}
配置全局事务中局部不受事务控制
# 1.不受全局事务控制
# 如有多个数据库,让使用db的视图不受事务控制
@transaction.non_atomic_requests(using='db')
def my_other_view(request):
pass
回滚点配置保存及提交
sid = transaction.savepoint() # 回滚点
with transaction.atomic():
try:
pass
except Exception as e:
# 如发生异常,回滚到指定地方。
transaction.savepoint_rollback(sid)
# 如果没有异常,显式地提交一次事务
transaction.savepoint_commit(sid)
悲观锁的实现
# django实现悲观锁
@transaction.atomic
def post(self, request):
# select_for_update表示锁,只有获取到锁才会执行查询,否则阻塞等待。
xxx.objects.select_for_update().get(id=1)
# 等事务提交后,会自动释放锁。
# 同时使用select_for_update与select_related
# 只会锁定entry(self)和category,不会锁定作者author
xxx.objects.select_related('author', 'category').select_for_update(of=('self', 'category'))
# 注意:MySQL版本要在8.0.1 以上才支持 nowait,skip_locked和of选项。
乐观锁的实现
# 乐观锁实现
@transaction.atomic
def post(self, request):
goods_id = request.GET.get('id')
count = int(request.GET.get('count'))
while True:
# 查询商品对象 -- 最基本查询
goods = xxx.objects.filter(id=goods_id).first()
origin_stock = goods.stock # 获取原始库存,会检查是否有变化,乐观锁的特点
sleep(5)
# 减少商品的库存数量,保存到数据库
xxx.objects.filter(id=goods_id, stock=origin_stock).update(stock=origin_stock - count)
mysql 的主从搭建的原因:
1.实现读写分离,多个msyql可实现提高并发量
2.主库写数据,从库读数据
docker
pull mysql:5.7
创建文件夹,配置mysql1主,mysql2从数据库
mkdir /root/mysql1
mkdir /root/mysql1/conf.d
mkdir /root/mysql2/data/
touch /root/mysql2/my.cnf
mkdir /root/mysql2
mkdir /root/mysql2/conf.d
mkdir /root/mysql2/data/
touch /root/mysql2/my.cnf
配置主从数据库
主:/root/mysql1/my.cnf
主
[mysqld]
user=mysql
character-set-server=utf8
default_authentication_plugin=mysql_native_password
secure_file_priv=/var/lib/mysql
expire_logs_days=7
sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
max_connections=1000
server-id=100 # id
log-bin=mysql-bin # bin
[client]
default-character-set=utf8
[mysql]
default-character-set=utf8
从:/root/mysql2/my.cnf
[mysqld]
user=mysql
character-set-server=utf8
default_authentication_plugin=mysql_native_password
secure_file_priv=/var/lib/mysql
expire_logs_days=7
sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
max_connections=1000
server-id=101 # id
log-bin=mysql-slave-bin # bin
relay_log=edu-mysql-relay-bin
[client]
default-character-set=utf8
[mysql]
default-character-set=utf8
启动mysql,设置端口和目录的映射
主库容器设置
docker run -di -v /root/mysql1/data/:/var/lib/mysql -v /root/mysql1/conf.d:/etc/mysql/conf.d -v /root/mysql1/my.cnf:/etc/mysql/my.cnf -p 33307:3306 --name mysql-master -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7
从库容器设置
docker run -di -v /root/mysql2/data/:/var/lib/mysql -v /root/mysql2/conf.d:/etc/mysql/conf.d -v /root/mysql2/my.cnf:/etc/mysql/my.cnf -p 33306:3306 --name mysql-slave -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7
docker ps 可查看运行的mysql容器
连接主库
mysql -uroot -P33307 -h 10.0.0.200 -p123456
再主库创建用户并授予所以权限
## 创建用户test
create user 'test'@'%' identified by '123';
## 授权
grant all privileges on *.* to 'test'@'%' ;
## 刷新权限
flush privileges;
# 查看主状态
show master status;
连接从数据库
change master to master_host='10.0.0.200',master_port=33307,master_user='test',master_password='123',master_log_file='mysql-bin.000003',master_log_pos=0;
命令详细解释
- change master to
- master_host='MySQL主服务器IP地址',
- master_user='之前在MySQL主服务器上面创建的用户名',
- master_password='之前创建的密码',
- master_log_file='MySQL主服务器状态中的二进制文件名',
- master_log_pos='MySQL主服务器状态中的position值';
这时在主数据库中写入数据,可在从数据库中查看;
注若测试在从数据库中写入数据会导致主从冲突,造成主从断开
可以通过下发如下命令解决
stop slave; # 先停止主从库
set GLOBAL SQL_SLAVE_SKIP_COUNTER=2; # 这里是跳过binlog的命令条数
start slave; # 启动主从就ok了
django的多数据的读写分离
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
},
'db1': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db1.sqlite3',
}
}
migrate --database db1 app01 # 迁移指定数据库
手动测试读写分离:
Book.objects.using('db1').create(name='xxx')
自动测试读写分离:
class DBRouter(object):
def db_for_read(self, model, **hints):
# 多个从库 ['db1','db2','db3']
# model是哪个模型表
return 'db1'
def db_for_write(self, model, **hints):
return 'default'
DATABASE_ROUTERS = ['utils.db_router.DBRouter', ] # 配置setting
借鉴博客:https://www.cnblogs.com/clever-cat/p/17356820.html