mysql是一个c/s架构的数据库管理系统,
客户端可以是图形化界面,也可以是命令行或者java等程序
服务端由一下组成部分
可插拔的存储引擎
文件系统与日志
目的:
1.降级冗余
id
(员工编号)、name
(员工名称)、mobile
(电话)、zip
(邮编)、province
(省份)、city
(城市)、district
(区县)、deptNo
(所属部门编号)、deptName
(所属部门名称)
员工表的province、city、district依赖于zip
应该拆分为用户表和区域表
用户表:id
(员工编号)、name
(员工名称)、mobile
(电话)、deptNo
(所属部门编号)、deptName
(所属部门名称)
区域表:zip
(邮编)、province
(省份)、city
(城市)、district
(区县) 省市区依赖与邮编,邮编是主键
三范式存在的意义:尽可能的减少数据的冗余,三范式只是参考,实际可以适当的冗余
结论
char:固定长度,不够会在末尾补空格,取出时删除所有末尾的空格,所以取出时会丢失末尾的空格,可能会浪费空间,查询效率比varchar高,单位字符,最多存255个字符,和字符集无关.
varchar:可变长度,存储实际字符串,不会浪费磁盘空间,查询效率比char慢,4.0前varchar(20)的单位为字节,5.0后为字符,最大存储的字符和存储引擎,字符集,当前行的其他列占用字节数有关.
固定长度 & 可变长度
CHAR类型用于存储固定长度字符串,比varchar类型查询效率更高.
VARCHAR类型用于存储可变长度字符串,它比固定长度类型更节省磁盘空间.
存储方式
char类型用空格进行剩余长度填充,取出时会丢失原字符串末尾的空格.
-- 建表语句
CREATE TABLE `str_table` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`str_char` char(10) DEFAULT NULL,
`str_varchar` varchar(10) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4;
-- 插入测试数据
INSERT INTO `str_table` (`id`, `str_char`, `str_varchar`)
VALUES
(null, '陈哈哈', '陈哈哈'),
(null, ' 陈哈哈', ' 陈哈哈'),
(null, '陈哈哈 ', '陈哈哈 ');
-- 测试数据查询
select id,concat("|",str_char,"|") as `char`,concat("|",str_varchar,"|") as `varchar` from str_table;
+----+---------------+---------------+
| id | char | varchar |
+----+---------------+---------------+
| 6 | |陈哈哈| | |陈哈哈| |
| 7 | | 陈哈哈| | | 陈哈哈| |
| 8 | |陈哈哈| | |陈哈哈 | |
+----+---------------+---------------+
3 rows in set (0.00 sec)
-- 结论:char会丢失字符串末尾的空格(猜测:存的时候在末尾补空格,取的时候删除了所有末尾的空格)
varchar类型需要额外存储1到2个字节的实际长度,长度小于等于255(28)时,占1字节;小于65535时(216),占2字节
存储容量
对于char类型来说,最多只能存放的字符个数为255,和编码无关,任何编码最大容量都是255。
MySQL行
默认最大65535字节,是所有列共享(相加)的,所以VARCHAR的最大值受此限制。
表中只有单列字段
情况下,varchar一般最多能存放(65535 - 3)个字节
为什么是65532个字符?算法如下(有余数时向下取整):
最大长度(字符数) = (行存储最大字节数 - NULL标识列占用字节数 - 长度标识字节数) / 字符集单字符最大字节数
NULL标识列占用字节数
:允许NULL时,占一字节长度标识字节数
:记录长度的标识,长度小于等于255(28)时,占1字节;小于65535时(216),占2字节字节
展示,如varchar(20),指的是20字节
;字符
展示。如varchar(20),指的是20字符
。以下为mysql5.7,存储引擎innodb,utf8字符集
(65535-3)/3=21844
21844-255=21589
因为255也要占用额外的空间存储长度和null标识,实际需要255*3+长度+null标识
就像使用VARCHAR(5)和VARCHAR(200)存储 '陈哈哈’的磁盘空间开销是一样的。那么使用更短的列有什么优势呢?
事实证明有很大的优势。更长的列会消耗更多的内存,因为MySQL通常会分配固定大小的内存块来保存内部值。当然,在没拿到存储引擎存储的数据之前,并不会知道我这一行拿出来的数据到底有多长,可能长度只有1,可能长度是500,那怎么办呢?那就只能先把最大空间分配好了,避免放不下的问题发生,这样实际上对于真实数据较短的varchar确实会造成空间的浪费。 举例:我向数据类型为:varchar(1000)的列插入了1024行数据,但是每个只存一个字符,那么这1024行真实数据量其实只有1K,但是我却需要约1M的内存去适应他。所以最好的策略是只分配真正需要的空间。 ———————————————— 版权声明:本文为CSDN博主「_陈哈哈」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/qq_39390545/article/details/109379218
因为varchar是可变字符串,所以实际存储是一样的,但是在没有从存储引擎拿到数据前,不知道给varchar分配多大的内存,所以会根据定于的长度先分配,所以varchar(20) 可能比varchar(10)占用更多的内存空间
mysql索引失效场景
B+树
B树一个节点存储的数据较少,要存储更多的数据,只能增加树的深度,也就增加了IO的次数
覆盖索引 是一种特殊类型的索引,它包含了查询所需的所有列(select 后面的和 where 后面的),因此可以完全覆盖查询的需求,无需回到原始数据页进行查找。这种索引可以提供更高效的查询性能,减少了磁盘I/O和数据访问的成本。
InnoDB | MyISAM | |
---|---|---|
事物 | 支持 | 不支持 |
行锁 | 支持 | 不支持 |
外键 | 支持 | 不支持 |
自动崩溃恢复 | 支持 | 不支持 |
存储方式 | 聚族索引与数据存一起(.frm和.ibd) | 索引和数据分开存*.frm,.MYD和.MYI |
---|---|---|
https://www.runoob.com/w3cnote/mysql-different-nnodb-myisam.html
延迟原因:
解决方案:
数据库读写分离是一种数据库架构模式,通过将读操作和写操作分离到不同的数据库实例或服务器上,以提高数据库的性能和可伸缩性。在数据库读写分离模式下,主数据库负责处理写操作(INSERT、UPDATE、DELETE),而从数据库负责处理读操作(SELECT)。
主要优势:
4大特性可以分为两部分:原子性(undo log)、一致性(undo log)、持久化(redo log),实际上是由InnoDB中的两份日志来保证的,一份是redo log日志,一份是undo log日志。 而隔离性是通过数据库的锁,加上MVCC来保证的(当前读是锁,快照读是MVCC)。
读未提交
读已提交(每次读取都生成一个读视图,会有幻读问题)
可重复读(默认级别 在mysql中不存在幻读 在开启事务时生成读视图,在当前事务里面复用第一次生成的读视图)
串行化(读写锁)
mysql InnoDB 默认隔离界别不会出现幻读演示
开启会话1->会话1读取->开启会话2->会话2插入一条数据->提交会话2->会话1读取(结果在会话2提交前后会话1读取到的是一致的)
会话1
mysql# begin;
Query OK, 0 rows affected (0.00 sec)
mysql# select * from t1;
+----+---------+
| id | content |
+----+---------+
| 1 | t1_926 |
+----+---------+
1 row in set (0.00 sec)
# 等待会话2提交事物,然后查询
mysql# select * from t1;
+----+---------+
| id | content |
+----+---------+
| 1 | t1_926 |
+----+---------+
1 row in set (0.00 sec)
会话2
mysql# begin;
Query OK, 0 rows affected (0.00 sec)
mysql# select * from t1;
+----+---------+
| id | content |
+----+---------+
| 1 | t1_926 |
+----+---------+
1 row in set (0.00 sec)
mysql# insert into t1(content)values('xxx');
Query OK, 1 row affected (0.00 sec)
mysql# select * from t1;
+----+---------+
| id | content |
+----+---------+
| 1 | t1_926 |
| 2 | xxx |
+----+---------+
2 rows in set (0.00 sec)
mysql# commit;
Query OK, 0 rows affected (0.01 sec)
mysql#
脏读:读其他事务未提交的数据(如果其他事务回滚数据就错误了)
不可重复读:在一个事务里面,两次读取到的数据列不一致(另一个事务修改了数据行,并且已经提交了)
幻读:在一个事务里面,两次读取到的行不一致(另一个事务新增或删除了数据,并且已经提交)
可重复读,但是在InnoDB中此隔离级别不存在幻读问题
MVCC(Multi-Version Concurrency Control)。它通过在事务中使用不同的版本来实现并发读写操作,从而提供了更好的并发性和隔离性。
基本原理(数据行隐藏字段+undo log版本链+读视图)
MVCC的优点包括:
表锁
行锁
间隙锁
临键锁:(]
元数据锁:只要有活动事务(即便是select),就不能修改表结构
意向锁:解决的是行锁与表锁的冲突(加表锁(共享与排他)是要判断有没有行锁,兼不兼容,逐行判断太效率低)
意向共享锁(IS): 由语句select … lock in share mode添加 。 与 表锁共享锁(read)兼容,与表锁排他锁(write)互斥。 意向排他锁(IX): 由insert、update、delete、select…for update添加 。与表锁共享锁(read)及排他锁(write)都互斥,意向锁之间不会互斥。
一旦事务提交了,意向共享锁、意向排他锁,都会自动释放。