1.参数配置调优。 先pass
2.索引调优
如果使用联合索引,要遵循最左匹配原则。 index(a,b,c) 如果想使用这个索引,那么必须遵循这个顺序使用。
模糊查询也是要遵循最左前缀匹配原则。 不能%在索引前 例如 %abc和%a%都是无效索引。
索引长度尽量端,长列可以建前缀索引
索引数据不宜频繁更新
索引不参与计算,
比如,where from_unixtime(create_time) = ’2014-05-29’就不能使用到索引,原因很简单,b+树中存的都是数据表中的字段值,但进行检索时,需要把所有元素都应用函数才能比较,显然成本太大。所以语句应该写成where create_time = unix_timestamp(’2014-05-29’)。
3.查询优化
使用join时,左表是进行全表扫描,所以尽量小表驱动大表。
避免对索引使用!=><操作,会造成全表扫描
减少select * 操作,可以用覆盖索引,查询的列过多可以不用索引。
如果对联合索引排序,也要遵循最左前缀原则,且升降一致。
如果查询中有某个列的范围查询,则其右边所有列都无法使用索引
A, B两个事务分别使用select ... where ... for update进行查询时:
- A事务执行查询操作的时候,如果这个查询结果为空,无论where条件是否是索引字段,B事务执行查询操作时,不会被阻塞。
- A事务执行查询操作的时候,当where条件是索引字段,则B事务执行同样的查询时会被行加锁阻塞;当where条件不是索引字段,则B事务执行有结果集的查询,都会被阻塞
这一部分设计到mysql的锁的问题,需要单独的去深入学习。
4.其他优化
开启慢查询日志,记录查询时间超时语句。
利用information_schema数据库的processlist表,实时查看执行时间过长的线程,定位需要优化的SQL。
例:select * from information_schema.processlist where Command != 'Sleep' order by Time desc;
垂直分割:
把一张表变为几张表,不常用的字段放在单独表中。
拆分执行时间长的Delete和insert语句。
避免在生产环境上执行会锁表的DELETE或INSERT的操作。一定把其拆分,或者使用LIMIT条件也是一个好的方法。
while (1) {
//每次只做1000条
mysql_query("DELETE FROM logs WHERE log_date <= '2009-11-01' LIMIT 1000");
if (mysql_affected_rows() == 0) {
// 没得可删了,退出! break; }
// 每次都要休息一会儿
usleep(50000);
}
Mysql的锁:
模式:乐观锁、悲观锁
按粒度:全局锁、表级锁、页级锁、行级锁
按属性:共享锁、排它锁
按状态:意向共享锁、意向排它锁
按算法:间隙锁(gap lock)、记录锁(record lock)、临键锁(next-key lock)
乐观锁 && 悲观锁:
乐观和悲观是对于数据会被修改的态度进行区分。是否保证数据的严格安全
乐观锁:
只在提交更新的时候去判断在此期间数据是否被更改。
适用场景:读多写少
实现:通过版本号,时间戳。拿数据做更新的时候对比版本号/时间戳是否一致,不一致不更。
Java JUC中的atomic包就是乐观锁的一种实现,AtomicInteger 通过CAS(Compare And Set)操作实现线程安全的自增。
悲观锁:
每次读都会上锁,直到锁被释放。
适用场景:读少写多,
实现:java synchronized
InnoDB采用两阶段锁定协议,在事务执行的过程中可以随时加锁,且只有执行commit/rollback才会解锁,innodb会根据事务隔离级别在需要的时候自动加锁。
显示锁定: select XXXX for update
select xxxxx lock in share
UPDATE 语句的WHERE 条件字句上需要建索引
举一个例子,下单扣库存判断,要避免超卖的出现。
如果是非高并发的情况下,可以这么写:
public boolean updateStockRaw(Long productId){ ProductStock product = query("SELECT * FROM tb_product_stock WHERE product_id=#{productId}", productId); if (product.getNumber() > 0) { int updateCnt = update("UPDATE tb_product_stock SET number=number-1 WHERE product_id=#{productId}", productId); if(updateCnt > 0){ //更新库存成功 return true; } } return false; }
这样仅仅是查出库存,库存不为空,扣库存,简单粗暴,但是高并发会有超卖问题。
悲观锁:
public boolean updateStock(Long productId){ //先锁定商品库存记录 ProductStock product = query("SELECT * FROM tb_product_stock WHERE product_id=#{productId} FOR UPDATE", productId); if (product.getNumber() > 0) { int updateCnt = update("UPDATE tb_product_stock SET number=number-1 WHERE product_id=#{productId}", productId); if(updateCnt > 0){ //更新库存成功 return true; } } return false; }
悲观锁在查库存的时候用select for update进行显示加锁。更新操作commit后释放锁。
乐观锁:
public boolean updateStock(Long productId){ int updateCnt = 0; while (updateCnt == 0) { ProductStock product = query("SELECT * FROM tb_product_stock WHERE product_id=#{productId}", productId); if (product.getNumber() > 0) { updateCnt = update("UPDATE tb_product_stock SET number=number-1 WHERE product_id=#{productId} AND number=#{number}", productId, product.getNumber()); if(updateCnt > 0){ //更新库存成功 return true; } } else { //卖完啦 return false; } } return false; }
乐观锁在查库存的时候放开权限,随便查,但是在更新的时候拿库存数作为比较标识确认前后操作之间是否库存有变动。
乐观锁的思路一般是表中增加版本字段,更新时where语句中增加版本的判断,算是一种CAS(Compare And Swep)操作
解决并发更新库存问题,除了用乐观锁悲观锁,我们还可以用分布式锁。例如基于Redis的和Zookeeper的
Redisson Distributed locks 、 Apache Curator Shared Lock
明天看一下CAS和这两个分布式锁,然后再回到数据库的锁学习上。
标签:product,乔亚,updateCnt,--,查询,索引,Mysql,WHERE,productId From: https://www.cnblogs.com/dwj-ngu/p/17127612.html