数据库
数据库
数据库是以一定方式储存在一起、能与多个用户共享、具有尽可能小的冗余度、与应用程序彼此独立的数据集合,可视为电子化的文件柜——存储电子文件的处所,用户可以对文件中的数据进行新增、查询、更新、删除等操作。
数据库是存放数据的仓库。它的存储空间很大,可以存放百万条、千万条、上亿条数据。但是数据库并不是随意地将数据进行存放,是有一定的规则的,否则查询的效率会很低。当今世界是一个充满着数据的互联网世界,充斥着大量的数据。即这个互联网世界就是数据世界。数据的来源有很多,比如出行记录、消费记录、浏览的网页、发送的消息等等。除了文本类型的数据,图像、音乐、声音都是数据。
参考资料来源:百度百科-数据库
数据库的安装
orcale数据库
MySQL数据库
索引
参考资料来源:百度百科-数据库索引
一、数据索引是干什么用的呢?
数据库索引其实就是为了使查询数据效率快。
二、数据库索引有哪些呢?
1.聚集索引(主键索引):在数据库里面,所有行数都会按照主键索引进行排序。
2.非聚集索引:就是给普通字段加上索引。
3.联合索引:就是好几个字段组成的索引,称为联合索引。
key 'idx_age_name_sex' ('age','name','sex')
联合索引遵从最左前缀原则,什么意思呢,就比如说一张学生表里面的联合索引如上面所示,那么下面A,B,C,D,E,F哪个会走索引呢?
1.A:select * from student where age = 16 and name = '小张'
2.B:select * from student where name = '小张' and sex = '男'
3.C:select * from student where name = '小张' and sex = '男' and age = 18
4.D:select * from student where age > 20 and name = '小张'<br>E:select * from student where age != 15 and name = '小张'<br>F:select * from student where age = 15 and name != '小张'
- A遵从最左匹配原则,age是在最左边,所以A走索引;
- B直接从name开始,没有遵从最左匹配原则,所以不走索引;
- C虽然从name开始,但是有索引最左边的age,mysql内部会自动转成where age = ‘18’ and name = ‘小张’ and sex = ‘男’ 这种,所以还是遵从最左匹配原则;
- D这个是因为age>20是范围,范围字段会结束索引对范围后面索引字段的使用,所以只有走了age这个索引;
三、有哪些列子不走索引呢?
表student中两个字段age,name加了索引
key 'idx_age' ('age'),
key 'idx_name' ('name')
- Like这种就是%在前面的不走索引,在后面的走索引
A:select * from student where 'name' like '王%'
B:select * from student where 'name' like '%小'
A走索引,B不走索引
- 用索引列进行计算的,不走索引
A:select * from student where age = 10+8
B:select * from student where age + 8 = 18
A走索引,B不走索引
- 对索引列用函数了,不走索引
A:select * from student where concat('name','哈') ='王哈哈';
B:select * from student where name = concat('王哈','哈');
A不走索引,B走索引
- 索引列用了!= 不走索引,如下:
select * from student where age != 18
四、为什么索引用B+树
这个可以参考什么是B+树
五、索引在磁盘上的存储?
聚集索引和非聚集索引存储的不相同,那么来说下都是怎么存储的?
有一张学生表
create table `student` (
`id` int(11) not null auto_increment comment '主键id',
`name` varchar(50) not null default '' comment '学生姓名',
`age` int(11) not null default 0 comment '学生年龄',
primary key (`id`),
key `idx_age` (`age`),
key `idx_name` (`name`)
) ENGINE=InnoDB default charset=utf8 comment ='学生信息';
表中内容如下
id | name | age |
---|---|---|
1 | 小王 | 12 |
2 | 小张 | 13 |
3 | 小刘 | 10 |
4 | 小徐 | 15 |
5 | 小高 | 11 |
6 | 小郑 | 14 |
7 | 小毛 | 17 |
id 为主键索引,name和age为非聚集索引
1.聚集索引在磁盘中的存储
聚集索引叶子结点存储是表里面的所有行数据;
每个数据页在不同的磁盘上面;
如果要查找id=5的数据,那么先把磁盘0读入内存,然后用二分法查找id=5的数在3和6之间,然后通过指针p1查找到磁盘2的地址,然后将磁盘2读入内存中,用二分查找方式查找到id=5的数据。
2.非聚集索引在磁盘中的存储
叶子结点存储的是聚集索引键,而不存储表里面所有的行数据,所以在查找的时候,只能查找到聚集索引键,再通过聚集索引去表里面查找到数据。
如果要查找到name = 小徐,首先将磁盘0加载到内存中,然后用二分查找的方法查到在指针p1所指的地址上,然后通过指针p1所指的地址可知道在磁盘2上面,然后通过二分查找法得知小徐id=4;
然后在根据id=4将磁盘0加载到内存中,然后通过二分查找的方法查到在指针p1所指的地址上,然后通过指针p1所指的地址可知道在磁盘2上面,然后通过id=4查找出郑正行数据,就查找出name=小徐的数据了。
数据库的优化
一: 优化方向
1. SQL以及索引的优化
需要先写出良好的sql然后根据sql建表设计索引(索引不要设计的太多,会影响写入的效率,读的效率也会有所降低)索引也是会占据内存空间的
2. 合理的数据库是设计
根据数据库三范式来进行表结构的设计。设计表结构时,就需要考虑如何设计才能更有效的查询。
数据库三范式:
第一范式:数据表中每个字段都必须是不可拆分的最小单元,也就是确保每一列的原子性;
第二范式:满足一范式后,表中每一列必须有唯一性,都必须依赖于主键;
第三范式:满足二范式后,表中的每一列只与主键直接相关而不是间接相关(外键也是直接相关),字段没有冗余。
有时候可以根据场景合理地反规范化:
A:分割表。
B:保留冗余字段。当两个或多个表在查询中经常需要连接时,可以在其中一个表上增加若干冗余的字段,以 避免表之间的连接过于频繁,
一般在冗余列的数据不经常变动的情况下使用。
C:增加派生列。派生列是由表中的其它多个列的计算所得,增加派生列可以减少统计运算,在数据汇总时可以大大缩短运算时间。
数据库五大约束:
A:PRIMARY key:设置主键约束;
B:UNIQUE:设置唯一性约束,不能有重复值;
C:DEFAULT 默认值约束
D:NOT NULL:设置非空约束,该字段不能为空;
E:FOREIGN key :设置外键约束。
字段类型选择:
A:尽量使用TINYINT、SMALLINT、MEDIUM_INT作为整数类型而非INT,如果非负则加上UNSIGNED
B:VARCHAR的长度只分配真正需要的空间
C:使用枚举或整数代替字符串类型
D:尽量使用TIMESTAMP而非DATETIME
E:单表不要有太多字段,建议在20以内
F:避免使用NULL字段,很难查询优化且占用额外索引空间
3. 系统配置的优化
1.数据库是基于操作系统的,目前大多数MySQL都是安装在linux系统之上,所以对于操作系统的一些参数配置也会影响到M ySQL的性能,下面就列出一些常用的系统配置。
2.操作系统的优化
网络方面的配置,要修改/etc/sysctl.conf
①、增加tcp支持的队列数
net.ipv4.tcp_max_syn_backlog = 65535//
②、减少断开连接时,资源回收(tcp有连接状态)
net.ipv4.tcp_max_tw_buckets = 8000 //
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_fin_timeout = 10
说明: TCP是有连接状态,通过netstat查看连接状态,经常会看到timeout状态或者timewait状态连接,为了加快timewait状态的连接回收,就需要调整上面的四个参数,保持TCP连接数在一个适当的状态。
3.打开文件数的限制,可以使用ulimit –a查看目录的各个限制,可以修改/etc/security/limits.conf文件,增加以下内容以修改打开文件数量的限制(永久生效)
*Soft nofile 65535
*Hard nofile 65535
以上配置需要根据服务硬件配置来设置,慎用!!!!!
4.MySQL配置文件优化
Mysql可以通过启动时指定参数和使用配置文件两种方法进行配置,在大多数情况下配置文件位于/etc/my.cnf 或者是 /etc/mysql/my.cnf在Windows系统配置文件可以是位于C://windows//my.ini文件,MySQL查找配置文件的顺序可以通过以下方法获得。
/usr/sbin/mysqld --verbose --help | grep -A 1 ‘default options’
注意:如果存在多个位置存在配置文件,则后面的会覆盖前面的。
4.1 MySQL配置文件-常用参数说明
max_connections
MySQL的最大连接数,增加该值增加mysqld 要求的文件描述符的数量。如果服务器的并发连接请求量比较大,建议调高此值,以增加并行连接数量,当然这建立在机器能支撑的情况下,因为如果连接数越多,介于MySQL会为每个连接提供连接缓冲区,就会开销越多的内存,所以要适当调整该值,不能盲目提高设值。
数值过小会经常出现ERROR 1040: Too many connections错误,可以过’conn%’通配符查看当前状态的连接数量,以定夺该值的大小。
show variables like ‘max_connections’ 最大连接数
show status like ‘max_used_connections’响应的连接数
1.理想值设置为多大才合适了?
max_used_connections / max_connections * 100% (理想值≈ 85%)
如果max_used_connections跟max_connections相同 那么就是max_connections设置过低或者超过服务器负载上限了,低于10%则设置过大。
4.2 back_log
MySQL能暂存的连接数量。当主要MySQL线程在一个很短时间内得到非常多的连接请求,这就起作用。如果MySQL的连接数据达到max_connections时,新来的请求将会被存在堆栈中,以等待某一连接释放资源,该堆栈的数量即back_log,如果等待连接的数量超过back_log,将不被授予连接资源。
back_log值指出在MySQL暂时停止回答新请求之前的短时间内有多少个请求可以被存在堆栈中。只有如果期望在一个短时间内有很多连接,你需要增加它,换句话说,这值对到来的TCP/IP连接的侦听队列的大小。
当观察你主机进程列表(mysql> show full processlist),发现大量 unauthenticated user | xxx.xxx.xxx.xxx | NULL | Connect | NULL | login | NULL 的待连接进程时,就要加大back_log 的值了。
默认数值是50,可调优为128,对于Linux系统设置范围为小于512的整数。
4.3 interactive_timeout
一个交互连接在被服务器在关闭前等待行动的秒数。一个交互的客户被定义为对mysql_real_connect()使用CLIENT_INTERACTIVE 选项的客户。
默认数值是28800,可调优为7200。
4.4 缓冲区变量
4.4.1 key_buffer_size:全局缓冲
key_buffer_size指定索引缓冲区的大小,它决定索引处理的速度,尤其是索引读的速度。通过检查状态值Key_read_requests和Key_reads,可以知道key_buffer_size设置是否合理。比例key_reads / key_read_requests应该尽可能的低,至少是1:100,1:1000更好(上述状态值可以使用SHOW STATUS LIKE ‘key_read%’获得)。
key_buffer_size只对MyISAM表起作用。即使你不使用MyISAM表,但是内部的临时磁盘表是MyISAM表,也要使用该值。可以使用检查状态值created_tmp_disk_tables得知详情。
show global status like ‘key_read%’;
举例:一共有27813678764个索引读取请求,有6798830个请求在内存中没有找到直接从硬盘读取索引,计算索引未命中缓存的概率:
key_cache_miss_rate =Key_reads / Key_read_requests * 100%,设置在1/1000左右较好
默认配置数值是8388600(8M),假如主机有4GB内存,可以调优值268435456(256MB)。
4.4.2、query_cache_size
使用查询缓冲,MySQL将查询结果存放在缓冲区中,今后对于同样的SELECT语句(区分大小写),将直接从缓冲区中读取结果。通过检查状态值Qcache_*,可以知道query_cache_size设置是否合理(上述状态值可以使用SHOW STATUS LIKE ‘Qcache%’获得)。如果Qcache_lowmem_prunes的值非常大,则表明经常出现缓冲不够的情况,如果Qcache_hits的值也非常大,则表明查询缓冲使用非常频繁,此时需要增加缓冲大小;如果Qcache_hits的值不大,则表明你的查询重复率很低,这种情况下使用查询缓冲反而会影响效率,那么可以考虑不用查询缓冲。此外,在SELECT语句中加入SQL_NO_CACHE可以明确表示不使用查询缓冲。
与查询缓冲有关的参数还有query_cache_type、query_cache_limit、query_cache_min_res_unit。
query_cache_type指定是否使用查询缓冲,可以设置为0、1、2,该变量是SESSION级的变量。
query_cache_limit指定单个查询能够使用的缓冲区大小,缺省为1M。
query_cache_min_res_unit是在4.1版本以后引入的,它指定分配缓冲区空间的最小单位,缺省为4K。检查状态值Qcache_free_blocks,如果该值非常大,则表明缓冲区中碎片很多,这就表明查询结果都比较小,此时需要减小query_cache_min_res_unit。
举例如下:
show global status like ‘qcache%’;
查询缓存碎片率= Qcache_free_blocks / Qcache_total_blocks * 100%
如果查询缓存碎片率超过20%,可以用FLUSH QUERY CACHE整理缓存碎片,或者试试减小query_cache_min_res_unit,如果你的查询都是小数据量的话。
查询缓存利用率= (query_cache_size – Qcache_free_memory) / query_cache_size * 100%
查询缓存利用率在25%以下的话说明query_cache_size设置的过大,可适当减小;查询缓存利用率在80%以上而且Qcache_lowmem_prunes > 50的话说明query_cache_size可能有点小,要不就是碎片太多。
查询缓存命中率= (Qcache_hits – Qcache_inserts) / Qcache_hits * 100%
示例服务器查询缓存碎片率=20.46%,查询缓存利用率=62.26%,查询缓存命中率=1.94%,命中率很差,可能写操作比较频繁吧,而且可能有些碎片。
4.5 record_buffer_size
每个进行一个顺序扫描的线程为其扫描的每张表分配这个大小的一个缓冲区。如果你做很多顺序扫描,你可能想要增加该值。
默认数值是131072(128K),可改为16773120 (16M)
4.6 read_rnd_buffer_size
随机读缓冲区大小。当按任意顺序读取行时(例如,按照排序顺序),将分配一个随机读缓存区。进行排序查询时,MySQL会首先扫描一遍该缓冲,以避免磁盘搜索,提高查询速度,如果需要排序大量数据,可适当调高该值。但MySQL会为每个客户连接发放该缓冲空间,所以应尽量适当设置该值,以避免内存开销过大。一般可设置为16M
4.7 sort_buffer_size
每个需要进行排序的线程分配该大小的一个缓冲区。增加这值加速ORDER BY或GROUP BY操作。
默认数值是2097144(2M),可改为16777208 (16M)。
4.8 join_buffer_size
联合查询操作所能使用的缓冲区大小。
record_buffer_size,read_rnd_buffer_size,sort_buffer_size,join_buffer_size为每个线程独占,也就是说,如果有100个线程连接,则占用为16M*100
4.9 table_cache
表高速缓存的大小。每当MySQL访问一个表时,如果在表缓冲区中还有空间,该表就被打开并放入其中,这样可以更快地访问表内容。通过检查峰值时间的状态值Open_tables和Opened_tables,可以决定是否需要增加table_cache的值。如果你发现open_tables等于table_cache,并且opened_tables在不断增长,那么你就需要增加table_cache的值了(上述状态值可以使用SHOW STATUS LIKE ‘Open%tables’获得)。注意,不能盲目地把table_cache设置成很大的值。如果设置得太高,可能会造成文件描述符不足,从而造成性能不稳定或者连接失败。
1G内存机器,推荐值是128-256。内存在4GB左右的服务器该参数可设置为256M或384M。
4.10 max_heap_table_size
用户可以创建的内存表(memory table)的大小。这个值用来计算内存表的最大行数值。这个变量支持动态改变,即set @max_heap_table_size=#
这个变量和tmp_table_size一起限制了内部内存表的大小。如果某个内部heap(堆积)表大小超过tmp_table_size,MySQL可以根据需要自动将内存中的heap表改为基于硬盘的MyISAM表。
4.11 tmp_table_size
通过设置tmp_table_size选项来增加一张临时表的大小,例如做高级GROUP BY操作生成的临时表。如果调高该值,MySQL同时将增加heap表的大小,可达到提高联接查询速度的效果,建议尽量优化查询,要确保查询过程中生成的临时表在内存中,避免临时表过大导致生成基于硬盘的MyISAM表。
show global status like ‘created_tmp%’;
每次创建临时表,Created_tmp_tables增加,如果临时表大小超过tmp_table_size,则是在磁盘上创建临时表,Created_tmp_disk_tables也增加,Created_tmp_files表示MySQL服务创建的临时文件文件数,比较理想的配置是:
Created_tmp_disk_tables / Created_tmp_tables * 100% <= 25%比如上面的服务器Created_tmp_disk_tables / Created_tmp_tables * 100% =1.20%,应该相当好了
默认为16M,可调到64-256最佳,线程独占,太大可能内存不够I/O堵塞
4.12 thread_cache_size
可以复用的保存在中的线程的数量。如果有,新的线程从缓存中取得,当断开连接的时候如果有空间,客户的线置在缓存中。如果有很多新的线程,为了提高性能可以这个变量值。
通过比较 Connections和Threads_created状态的变量,可以看到这个变量的作用。默认值为110,可调优为80。
4.13 thread_concurrency
推荐设置为服务器 CPU核数的2倍,例如双核的CPU, 那么thread_concurrency的应该为4;2个双核的cpu, thread_concurrency的值应为8。默认为8
4.14 wait_timeout
指定一个请求的最大连接时间,对于4GB左右内存的服务器可以设置为5-10。
4.5 配置InnoDB的几个变量
4.5.1、innodb_buffer_pool_size
对于InnoDB表来说,innodb_buffer_pool_size的作用就相当于key_buffer_size对于MyISAM表的作用一样。InnoDB使用该参数指定大小的内存来缓冲数据和索引。对于单独的MySQL数据库服务器,最大可以把该值设置成物理内存的80%。
根据MySQL手册,对于2G内存的机器,推荐值是1G(50%)。
show status like ‘innodb%’;
4.5.2、innodb_flush_log_at_trx_commit
主要控制了innodb将log buffer中的数据写入日志文件并flush磁盘的时间点,取值分别为0、1、2三个。0,表示当事务提交时,不做日志写入操作,而是每秒钟将log buffer中的数据写入日志文件并flush磁盘一次;1,则在每秒钟或是每次事物的提交都会引起日志文件写入、flush磁盘的操作,确保了事务的ACID;设置为2,每次事务提交引起写入日志文件的动作,但每秒钟完成一次flush磁盘操作。
实际测试发现,该值对插入数据的速度影响非常大,设置为2时插入10000条记录只需要2秒,设置为0时只需要1秒,而设置为1时则需要229秒。因此,MySQL手册也建议尽量将插入操作合并成一个事务,这样可以大幅提高速度。
根据MySQL手册,在允许丢失最近部分事务的危险的前提下,可以把该值设为0或2。
4.5.3、innodb_log_buffer_size
log缓存大小,一般为1-8M,默认为1M,对于较大的事务,可以增大缓存大小。可设置为4M或8M。
4.5.4、innodb_additional_mem_pool_size
该参数指定InnoDB用来存储数据字典和其他内部数据结构的内存池大小。缺省值是1M。通常不用太大,只要够用就行,应该与表结构的复杂度有关系。如果不够用,MySQL会在错误日志中写入一条警告信息。
根据MySQL手册,对于2G内存的机器,推荐值是20M,可适当增加。
innodb_thread_concurrency=8
推荐设置为 2*(NumCPUs+NumDisks),默认一般为8
[client]
port = 3306
socket = /tmp/mysql.sock
[mysqld]
port = 3306
socket = /tmp/mysql.sock
basedir = /usr/local/mysql
datadir = /data/mysql
pid-file = /data/mysql/mysql.pid
user = mysql
bind-address = 0.0.0.0
server-id = 1 #表示是本机的序号为1,一般来讲就是master的意思
再例如:MySQL数据库my.cnf
4. 硬件优化
更快的IO、更多的内存。一般来说内存越大,对于数据库的操作越好。但是CPU多就不一定了,因为他并不会用到太多的CPU数量,有很多的查询都是单CPU。另外使用高的IO(SSD、RAID),但是IO并不能减少数据库锁的机制。所以说如果查询缓慢是因为数据库内部的一些锁引起的,那么硬件优化就没有什么意义。
二:优化方案
代码优化
之所以把代码放到第一位,是因为这一点最容易引起技术人员的忽视。很多技术人员拿到一个性能优化的需求以后,言必称缓存、异步、JVM等。实际上,第一步就应该是分析相关的代码,找出相应的瓶颈,再来考虑具体的优化策略。有一些性能问题,完全是由于代码写的不合理,通过直接修改一下代码就能解决问题的,比如for循环次数过多、作了很多无谓的条件判断、相同逻辑重复多次等。
举个例子:
一个update操作,先查询出entity,再执行update,这样无疑多了一次数据库交互。还有一个问题,update语句可能会操作一些无需更新的字段。
我们可以将表单中涉及到的属性,以及updateTime,updateUser等赋值到entity,直接通过pdateByPrimaryKeySelective,去update特定字段。
updateByPrimaryKeySelective会对字段进行判断再更新(如果为Null就忽略更新),如果你只想更新某一字段,可以用这个方法。
updateByPrimaryKey对你注入的字段全部更新
定位慢SQL,并优化
这是最常用、每一个技术人员都应该掌握基本的SQL调优手段(包括方法、工具、辅助系统等)。这里以MySQL为例,最常见的方式是,由自带的慢查询日志或者开源的慢查询系统定位到具体的出问题的SQL,然后使用explain、profile等工具来逐步调优,最后经过测试达到效果后上线。
SqlServer执行计划:
通过执行计划,我们能得到哪些信息:
A:哪些步骤花费的成本比较高
B:哪些步骤产生的数据量多,数据量的多少用线条的粗细表示,很直观
C:每一步执行了什么动作
具体优化手段:
A:尽量少用(或者不用)sqlserver 自带的函数
select id from t where substring(name,1,3) = ’abc’
select id from t where datediff(day,createdate,’2005-11-30′) = 0
可以这样查询:
select id from t where name like ‘abc%’
select id from t where createdate >= ‘2005-11-30’ and createdate < ‘2005-12-1’
B:连续数值条件,用BETWEEN不用IN:SELECT id FROM t WHERE num BETWEEN 1 AND 5
C:Update 语句,如果只更改1、2个字段,不要Update全部字段,否则频繁调用会引起明显的性能消耗
D:尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型
E:不建议使用 select * from t ,用具体的字段列表代替“*”,不要返回用不到的任何字段。尽量避免向客户 端返回大数据量,若数据
量过大,应该考虑相应需求是否合理
F:表与表之间通过一个冗余字段来关联,要比直接使用JOIN有更好的性能
G:select count(*) from table;这样不带任何条件的count会引起全表扫描
连接池调优
我们的应用为了实现数据库连接的高效获取、对数据库连接的限流等目的,通常会采用连接池类的方案,即每一个应用节点都管理了一个到
各个数据库的连接池。随着业务访问量或者数据量的增长,原有的连接池参数可能不能很好地满足需求,这个时候就需要结合当前使用连接
池的原理、具体的连接池监控数据和当前的业务量作一个综合的判断,通过反复的几次调试得到最终的调优参数。
合理使用索引
索引一般情况下都是高效的。但是由于索引是以空间换时间的一种策略,索引本身在提高查询效率的同时会影响插入、更新、删除
的效率,频繁写的表不宜建索引。
选择合适的索引列,选择在where,group by,order by,on从句中出现的列作为索引项,对于离散度不大的列没有必要创建索引。
主键已经是索引了,所以primay key 的主键不用再设置unique唯一索引
索引类型
主键索引 (PRIMARY KEY)
唯一索引 (UNIQUE)
普通索引 (INDEX)
组合索引 (INDEX)
全文索引 (FULLTEXT)
可以应用索引的操作符
大于等于
Between
IN
LIKE 不以 % 开头
不能应用索引的操作符
NOT IN
LIKE %_ 开头
如何选择索引字段
A:字段出现在查询条件中,并且查询条件可以使用索引
B:通常对数字的索引和检索要比对字符串的索引和检索效率更高
C:语句执行频率高,一天会有几千次以上
D:通过字段条件可筛选的记录集很小
无效索引
A:尽量不要在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描
B:应尽量避免在 where 子句中使用 != 或 <> 操作符,否则将引擎放弃使用索引而进行全表扫描。
C:应尽量避免在 where 子句中使用 or 来连接条件,如果一个字段有索引,一个字段没有索引,将导致引擎放弃使用索引而进行全表扫描
select id from t where num=10 or Name = ‘admin’
可以这样查询:
select id from t where num = 10
union
select id from t where Name = ‘admin’
union all 返回所有数据,不管是不是重复。 union会自动压缩,去除重复数据。
D:不做列运算
where age + 1 = 10,任何对列的操作都将导致表扫描,它包括数据库教程函数、计算表达式等
E:查询like,如果是 ‘%aaa’ 不会使用到索引
大数据量下的数据库查询与插入如何优化?
分表
分表方式
水平分割(按行)、垂直分割(按列)
分表场景
A: 根据经验,mysql表数据一般达到百万级别,查询效率就会很低。
B: 一张表的某些字段值比较大并且很少使用。可以将这些字段隔离成单独一张表,通过外键关联,例如考试成绩,我们通常关注分数,不关注考试详情。
水平分表策略
按时间分表:当数据有很强的实效性,例如微博的数据,可以按月分割。
按区间分表:例如用户表 1到一百万用一张表,一百万到两百万用一张表。
hash分表:通过一个原始目标id或者是名称按照一定的hash算法计算出数据存储的表名。
读写分离
当一台服务器不能满足需求时,采用读写分离【写: update/delete/add】的方式进行集群。
一台数据库支持最大连接数是有限的,如果用户的并发访问很多,一台服务器无法满足需求,可以集群处理。mysql集群处理技术最常用的就是读写分离。
主从同步:数据库最终会把数据持久化到磁盘,集群必须确保每个数据库服务器的数据是一致的。从库读主库写,从库从主库上同步数据。
读写分离:使用负载均衡实现,写操作都往主库上写,读操作往从服务器上读。
缓存
缓存分类
本地缓存:HashMap/ConcurrentHashMap、Ehcache、Guava Cache等
缓存服务:Redis/Tair/Memcache等
使用场景
短时间内相同数据重复查询多次且数据更新不频繁,这个时候可以选择先从缓存查询,查询不到再从数据库加载并回设到缓存的方式。此种场景较适合用单机缓存。
高并发查询热点数据,后端数据库不堪重负,可以用缓存来扛。
缓存作用
减轻数据库的压力,减少访问时间。
缓存选择
如果数据量小,并且不会频繁地增长又清空(这会导致频繁地垃圾回收),那么可以选择本地缓存。具体的话,如果需要一些策略的支持(比如缓存满的逐出策略),可以考虑Ehcache;如不需要,可以考虑HashMap;如需要考虑多线程并发的场景,可以考虑ConcurentHashMap。
其他情况,可以考虑缓存服务。目前从资源的投入度、可运维性、是否能动态扩容以及配套设施来考虑,我们优先考虑Tair。除非目前Tair还不能支持的场合(比如分布式锁、Hash类型的value),我们考虑用Redis。
缓存穿透
一般的缓存系统,都是按照key去缓存查询,如果不存在对应的value,就应该去后端系统查找(比
如DB)。如果key对应的value是一定不存在的,并且对该key并发请求量很大,就会对后端系统造
成很大的压力。这就叫做缓存穿透。
对查询结果为空的情况也进行缓存,缓存时间设置短点,或者该key对应的数据insert了之后清理缓存。
缓存并发
有时候如果网站并发访问高,一个缓存如果失效,可能出现多个进程同时查询DB,同时设置缓存的情况,
如果并发确实很大,这也可能造成DB压力过大,还有缓存频繁更新的问题。
对缓存查询加锁,如果KEY不存在,就加锁,然后查DB入缓存,然后解锁;其他进程如果发现有锁就
等待,然后等解锁后返回数据或者进入DB查询。
缓存雪崩(失效)
当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,也会给后端系统(比如DB)
带来很大压力。
不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀.
防止缓存空间不够用
① 给缓存服务,选择合适的缓存逐出算法,比如最常见的LRU。
② 针对当前设置的容量,设置适当的警戒值,比如10G的缓存,当缓存数据达到8G的时候,就开始发出报警,提前排查问题或者扩容。
③ 给一些没有必要长期保存的key,尽量设置过期时间。
我们看下图,在WebServer(Dao层)和DB之间加一层cache,这层cache一般选取的介质是内存,因为我们都知道存入数据库的数据都具有持久化的特点,那么读写会有磁盘IO的操作,内存的读写速度远比磁盘快得多。(选用存储介质,提高访问速度:内存>>磁盘;减少磁盘IO的操作,减少重复查询,提高吞吐量)
常用开源的缓存工具有:ehcache、memcache、redis。
ehcache 是一个纯Java的进程内缓存框架,hibernate使用其做二级缓存。同时,ehcache可以通过多播的方式实现集群。本人主要用于本地的缓存,数据库上层的缓存。
memcache是一套分布式的高速缓存系统,提供key-value这样简单的数据储存,可充分利用CPU多核,无持久化功能。在做web集群中可以用做session共享,页面对象缓存。
redis高性能的key-value系统,提供丰富的数据类型,单核CPU有抗并发能力,有持久化和主从复制的功能。本人主要使用redis的redis sentinel,根据不同业务分为多组。
redis注意事项
A:在增加 key 的时候尽量设置过期时间,不然 Redis Server 的内存使用会达到系统物理内存的最大值,导致 Redis 使用 VM 降低系统性能;
B:Redis Key 设计时应该尽可能短,Value 尽量不要使用复杂对象;
C:将对象转换成 JSON 对象(利用现成的 JSON 库)后存入 Redis;
D:将对象转换成 Google 开源二进制协议对象(Google Protobuf,和 JSON 数据格式类似,但是因为是二进制表现,所以性能效率以及空间占用都比 JSON 要小;缺点是 Protobuf 的学习曲线比 JSON 大得多);
E:Redis 使用完以后一定要释放连接。
读取缓存中是否有相关数据,如果缓存中有相关数据,则直接返回,这就是所谓的数据命中“hit”
如果缓存中没有相关数据,则从数据库读取相关数据,放入缓存中,再返回。这就是所谓的数据未命中“miss”
缓存的命中率 = 命中缓存请求个数/总缓存访问请求个数 = hit/(hit+miss)
NoSQL
与缓存的区别
先说明一下,这里介绍的和缓存不一样,虽然redis等也可以用来做数据存储方案(比如Redis或者Tair),但NoSql是把它作为DB来用。如果当作DB来用,需要有效保证数据存储方案的可用性、可靠性。
使用场景
需要结合具体的业务场景,看这块业务涉及的数据是否适合用NoSQL来存储,对数据的操作方式是否适合用NoSQL的方式来操作,或者是否需要用到NoSQL的一些额外特性(比如原子加减等)。
如果业务数据不需要和其他数据作关联,不需要事务或者外键之类的支持,而且有可能写入会异常频繁,这个时候就比较适合用NoSQL(比如HBase)。
比如,美团点评内部有一个对exception做的监控系统,如果在应用系统发生严重故障的时候,可能会短时间产生大量exception数据,这个时候如果选用MySQL,会造成MySQL的瞬间写压力飙升,容易导致MySQL服务器的性能急剧恶化以及主从同步延迟之类的问题,这种场景就比较适合用Hbase类似的NoSQL来存储。
视图/存储过程
普通业务逻辑尽量不要使用存储过程,定时任务或报表统计函数可以根据团队资源情况采用存储过程处理。
GVM调优
通过监控系统(如没有现成的系统,自己做一个简单的上报监控的系统也很容易)上对一些机器关键指标(gc time、gc count、各个分代的内存大小变化、机器的Load值与CPU使用率、JVM的线程数等)的监控报警,也可以看gc log和jstat等命令的输出,再结合线上JVM进程服务的一些关键接口的性能数据和请求体验,基本上就能定位出当前的JVM是否有问题,以及是否需要调优。
异步/多线程
针对某些客户端的请求,在服务端可能需要针对这些请求做一些附属的事情,这些事情其实用户并不关心或者用户不需要立即拿到这些事情的处理结果,这种情况就比较适合用异步的方式处理这些事情。
异步作用
A:缩短接口响应时间,使用户的请求快速返回,用户体验更好。
B:避免线程长时间处于运行状态,这样会引起服务线程池的可用线程长时间不够用,进而引起线程池任务队列长度增大,从而阻塞更多请求任务,使得更多请求得不到技术处理。
C:线程长时间处于运行状态,可能还会引起系统Load、CPU使用率、机器整体性能下降等一系列问题,甚至引发雪崩。异步的思路可以在不增加机器数和CPU数的情况下,有效解决这个问题。
异步实现
A:额外开辟线程,这里可以采用额外开辟一个线程或者使用线程池的做法,在IO线程(处理请求响应)之外的线程来处理相应的任务,在IO线程中让response先返回。
B:使用消息队列(MQ)中间件服务
搜索引擎
例如:solr,elasticsearch