文章目录
1.淘宝为鉴
LAMP:Linux+Apache+MySQL+PHP
垂直拆分:表拆分,将一个表拆成多个表,策略:按照时间,按照区域,用户ID等规则
水平拆分:多个数据库做复制,主从,集群等等。
QPS:Query Per Second
2.硬盘读写原理
硬盘
逻辑地址 -> 物理地址:盘面号 + 柱面号 + 扇区号
扇区大小512k,其标识符包含:盘面号 + 柱面号 + 扇区号
数据读取时间:寻道时间 + 旋转延迟 + 数据传输(磁盘到内存)
数据读取:cpu给出逻辑地址和内存地址,硬盘控制器翻译为物理地址并读取数据写入内存地址。
数据存储顺序:
系统将文件存储到磁盘上时,按柱面、磁头、扇区的方式进行,即最先是第1磁道的第一磁头下(也就是第1盘面的第一磁道)的所有扇区,然后,是同一柱面的下一磁头,……,一个柱面存储满后就推进到下一个柱面,直到把文件内容全部写入磁盘。系统将文件存储到磁盘上时,按柱面、磁头、扇区的方式进行,即最先是第1磁道的第一磁头下(也就是第1盘面的第一磁道)的所有扇区,然后,是同一柱面的下一磁头,……,一个柱面存储满后就推进到下一个柱面,直到把文件内容全部写入磁盘。
由于存储介质的特性,磁盘本身存取就比主存慢很多,再加上机械运动耗费,磁盘的存取速度往往是主存的几百分分之一,因此为了提高效率,要尽量减少磁盘I/O。为了达到这个目的,磁盘往往不是严格按需读取,而是每次都会预读,即使只需要一个字节,磁盘也会从这个位置开始,顺序向后读取一定长度的数据放入内存。
预读的长度一般为页(page)的整倍数。页是计算机管理存储器的逻辑块,硬件及操作系统往往将主存和磁盘存储区分割为连续的大小相等的块,每个存储块称为一页(在许多操作系统中,页得大小通常为4k),主存和磁盘以页为单位交换数据。当程序要读取的数据不在主存中时,会触发一个缺页异常,此时系统会向磁盘发出读盘信号,磁盘会找到数据的起始位置并向后连续读取一页或几页载入内存中,然后异常返回,程序继续运行。
数据库读数据过程:找到行数据的逻辑地址,CPU将该逻辑地址发给磁盘控制器,磁盘控制器将其翻译为物理地址后,在磁盘中找到相应的扇区进行读取,读取后的数据,写入内存。
数据库写数据过程:找到需要修改的行数据的逻辑地址,CPU将该逻辑地址发给磁盘控制器,磁盘控制器将其翻译为物理地址后,在磁盘中找到相应的扇区进行读取,读取后的数据,写入内存;在内存中修改数据,然后将数据写入磁盘。
3.总体架构
客户端发出sql查询;
服务器层的连接和线程处理模块,就会为其启动一个线程,并且做认证和权限控制;
如果查询缓存中有数据,直接返回,否则,进入解析器;
解析器将sql解析为MySQL内部存储结构(解析树);
优化器对执行的方案进行成本评估(该查询需要读取的行数),决定一个方案后,调用存储引擎;
存储引擎负责实际执行查询任务,和文件系统及硬件存储打交道。
每个客户端连接会在服务器进程中拥有有一个线程,服务器会缓存线程,实现线程池管理。
认证:用户名、原始主机信息和密码。如果使用SSL方式连接,还可以进行X.509证书认证。
权限:对某个表执行select/insert等权限。
4.并发控制
4.1.订单问题
并发创建订单时,修改库存数据。
DROP TABLE IF EXISTS `t_goods`;
CREATE TABLE `t_goods` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL COMMENT '名称',
`amount` int(11) NOT NULL COMMENT '商品库存数量',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of t_goods
-- ----------------------------
INSERT INTO `t_goods` VALUES ('1', '商品A', '15');
-- ----------------------------
-- Table structure for `t_order`
-- ----------------------------
DROP TABLE IF EXISTS `t_order`;
CREATE TABLE `t_order` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`goods_id` int(11) NOT NULL COMMENT '商品ID',
`amount` int(10) NOT NULL DEFAULT '0' COMMENT '商品数量',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
模拟并发时的问题脚本
select '创建订单...' as log;
insert into t_order (goods_id, amount) values (1, 1);
select '获取商品库存数量...' as log;
set @goods_amount = (select amount from t_goods where id = 1);
select '休眠10秒...' as log;
select sleep(10);
select '更新商品库存数量...' as log;
update t_goods set amount = (@goods_amount - 1) where id = 1;
模拟两个客户端访问数据过程:
SQL | Session1 | Session2 | 说明 | |
---|---|---|---|---|
1 | insert into t_order (goods_id, amount) values (1, 1); | 执行1 | 执行1 | |
2 | set @goods_amount = (select amount from t_goods where id = 1); | 执行2 | 执行2 | |
3 | select sleep(10); | 执行3 | 执行3 | |
4 | update t_goods set amoun |