存储引擎
现实生活中我们用来存储数据的文件应该有不同的类型:比如存文本用txt类型,存表格用excel,存图片用png等。数据库中的表也应该有不同的类型,表的类型不同,会对应mysql不同的存取机制,表类型又称为存储引擎。
存储引擎说白了就是如何存储数据、如何为存储的数据建立索引和如何更新、查询数据等技术的实现方法。因为在关系数据库中数据的存储是以表的形式存储的,所以存储引擎也可以称为表类型(即存储和操作此表的类型)
在Oracle 和SQL Server等数据库中只有一种存储引擎,所有数据存储管理机制都是一样的。而MySQL数据库提供了多种存储引擎。用户可以根据不同的需求为数据表选择不同的存储引擎,用户也可以根据自己的需要编写自己的存储引擎。
MySQL最常用的两个引擎:MyISAM 和 InnoDB 这里说一下二者的主要特性和区别。
MyISAM 支持全文索引,不支持事务、也不支持外键,所以访问速度较快。因此当对事务完整性没有要求并以访问(SELECT, INSERT)为主的应用适合使用该存储引擎
InnoDB 在支持事物,即支持具有提交、回滚及崩溃恢复能力等事务特性,所以比MyISAM存储引擎占用更多的磁盘空间,写的效率也差一些。因此当需要频繁的更新、删除操作,同时还对事务的完整性要求较高(类似计费系统或者财务系统等对数据准确性要求比较高的系统),需要实现并发控制,建议选择。
另外,二者对锁的支持也有不同,MyISAM 支持表锁,而InnoDB 支持行锁和表锁。
要对某个数据执行操作,在此期间不希望其它请求修改该数据时,就可以加锁。
MyISAM 表锁
MyISAM 表锁有两种:表共享读锁(Table Read Lock)和表独占写锁(Table Write Lock)。
- 表共享读锁:不会阻塞其它用户对同一表的读请求,但会阻塞对同一表的写请求。
- 表独占写锁:阻塞其它用户对同一表的读和写操作。
加锁和解锁:
-- 加读锁
LOCK TABLE tablename READ
-- 加写锁
LOCK TABLE tablename WRITE
-- 对数据库数据执行操作
-- SELECT ...
-- 解锁
UNLOCK TABLES
MyISAM在执行查询语句(SELECT)前,会自动给涉及的所有表加读锁,在执行更新操作(UPDATE、DELETE、INSERT等)前,会自动给涉及的表加写锁,这个过程并不需要用户干预,因此,用户一般不需要直接用LOCK TABLE命令给MyISAM表显式加锁。MyISAM表的读操作与写操作之间,以及写操作与写操作之间是串行的!
InnoDB 行锁和表锁
行锁
InnoDB 实现了以下两种类型的行锁:
- 共享锁:其它事物仍然可以查询,并获得共享锁,但是不能UPDATE 或者 DELETE 操作。一旦获得共享锁的当前事物需要对共享锁记录进行更新操作,而另一个事物也有共享锁时,需要等待锁,直到另一个事务死锁退出而获得锁。
- 排他锁:允许获得排他锁的事务更新数据,其它事物可以查询记录,但是不能加共享锁更不能更新数据据,要等待锁。
对于 UPDATE、 DELETE 和 INSERT 语句, InnoDB会自动给涉及数据集加排他锁;对于普通 SELECT 语句,InnoDB 不会加任何锁;
显示地给记录集加共享锁或排他锁。
SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE。 -- 共享锁
SELECT * FROM table_name WHERE ... FOR UPDATE。 -- 排它锁
行锁实现方式及注意事项
InnoDB 行锁是通过给索引上的索引项加锁来实现的,这一点 MySQL 与 Oracle 不同,后者是通过在数据块中对相应数据行加锁来实现的。InnoDB 这种行锁实现特点意味着:只有通过索引条件检索数据,InnoDB 才使用行级锁,否则,InnoDB 将使用表锁!
以下情形(id是主键索引)将会使用表锁:
根据索引未找到数据
SELECT * FROM table WHERE id=xx FOR UPDATE
根据索引找到的数不明确
SELECT * FROM table WHERE id>xx FOR UPDATE
根据非索引查找
SELECT * FROM table WHERE name = xx FOR UPDATE
在Django ORM中加锁
需要通过事务来实现:
from django.db import transaction # 导入事务
with transaction.atomic():
res = models.UserInfo.objects.filter(id=1).select_for_update()