程序员辛苦了
以下内容一针见效 简单粗暴直接有效
程序员的自救 —— 闪电创建索引
菜鸟:昨天跑的好好sql,今天怎么就慢了呢?DBA抓到慢查询了,在群里嗷嗷叫,真烦!
老鸟:小乖乖,加索引啊~
菜鸟:怎么加啊,人家看不懂执行计划啊~
老鸟:其他人都闪开,让我告诉你怎么快速建索引
快速建索引 TSQL
WITH MissingIndex AS
( SELECT db_name(c.database_id) AS DBName,c.statement,e.name AS SchemaName
,d.name AS TableName,a.user_seeks, avg_user_impact,c.equality_columns
, inequality_columns,c.included_columns
, IndexID=replace(newid(),'-',),CheckDate=getdate()
FROM sys.dm_db_missing_index_group_stats AS a
join sys.dm_db_missing_index_groups
AS b ON a.group_handle=b.index_group_handle
join sys.dm_db_missing_index_details AS c ON b.index_handle=c.index_handle j
oin sys.tables AS d ON c.[object_id]=d.[object_id] join sys.schemas e ON d.schema_id=e.schema_id )
SELECT *, "Create Index"= CASE
WHEN equality_columns is not null
THEN ('CREATE INDEX '+'IX_'+ TableName+'_'+indexID +' ON ['+SchemaName+'].[' +TableName+'] ('+equality_columns+ isnull(','+inequality_columns,) +')' +isnull(' INCLUDE ('+included_columns+')',))
WHEN equality_columns is null THEN ('CREATE INDEX '+'IX_'+ TableName+'_'+indexID +' ON ['+SchemaName+'].['+TableName+'] ('+inequality_columns+')' +isnull(' INCLUDE ('+included_columns+')',)) END
, dropIndex= ('DROP INDEX ['+SchemaName+'].['+TableName+'].'+'IX_'+TableName+'_'+indexID)
FROM MissingIndex ORDER BY user_seeks DESC
老鸟:此脚本一出,谁与争锋!大家看好了,我连创建索引的脚本都run出来了,copy出来就可以直接创建了。是不是很开森~~
随意建索引出现什么副作用呢? |
---|
1磁盘压力增大 |
2写操作性能变慢 |
3磁盘碎片增多 |
所以,索引不是拍拍脑袋随便加的,不是会写create index 就是会建索引,一定要结合业务!结合业务!结合业务!
|
想学摄影吗?—— 一键解决读写互斥
正所谓,两攻相遇必有一受 —— 近排吃瓜
with (nolock), 与READ_COMMITTED_SNAPSHOT 不可不说的二三事
Round One —— WITH (NOLOCK)
with(nolcok),不去请求锁,允许一定程度的脏读。实验代码如下:
CREATE DATABASE LockTestDB
USE LockTestDB
--生成测试表
CREATE TABLE LockTest
(
ID INT,
Value VARCHAR(20)
)
--插入测试数据
INSERT INTO LockTest VALUES (1,'Version1')
--而后新开一个Session1,执行语句
--执行更新,但是不提交
BEGIN TRAN
UPDATE LockTest SET Value='Version2' WHERE ID=1
--此时,新开一个Session2,执行语句
--无法查询TABLE1数据,因为Session 1没有提交
SELECT * FROM LockTest
--此时,新开一个Session3,执行语句
--可以查出
--ID Value
--————————
--1 Version2
-- 这就是为什么生产环境大多要求脚本中一定带有with(nolcok),不去请求锁,允许一定程度的脏读
SELECT * FROM LockTest with(nolock)
Round Two —— READ_COMMITTED_SNAPSHOT
READ_COMMITTED_SNAPSHOT,等同于orcale默认隔离级别
READ_COMMITTED_SNAPSHOT 一开,瞬间可以解决百万并发阻塞
但是,对TempDB数据库落磁盘性能要求高。如果TempDB所在磁盘性能垃圾,那么不如不开
实验如下:
--开启行版本隔离机制,乐观并发已提交读。等同于orcale默认隔离级别
--READ_COMMITTED_SNAPSHOT一开,瞬间可以解决百万并发阻塞
ALTER DATABASE LockTestDB SET READ_COMMITTED_SNAPSHOT ON
--而后新开一个Session1,执行语句
--执行更新,但是不提交
BEGIN TRAN
UPDATE LockTest SET Value='Version2' WHERE ID=1
--此时,新开一个Session2,执行语句
--可以查询出TABLE1数据
SELECT * FROM LockTest
--此时,虽然Session 1没有提交,但是照样可以查出
--ID Value
--————————
--1 Version1
--并且这个查询结果是干净的(Session1没有提交,所以Value当然是Version1)
穿上马甲照样认识你 —— 一键参数化
不加绑定变量的SQL是可怕的SQL
菜鸟:呵呵,我配置牛逼,不怕~
老鸟:呵呵哒,蚂蚁咬死象,晓得不~~
菜鸟:但是,如果现在改代码,来不及啊,还要release。
老鸟:可以一键参数化,先救火
实验如下:
CREATE DATABASE ParTest
USE ParTest
SELECT * FROM sys.objects WHERE object_id=1
SELECT * FROM sys.objects WHERE object_id=2
可以看到tsql分别编译执行了
接下来,一键参数化
-- PARAMETERIZATION 参数化
ALTER DATABASE ParTest SET PARAMETERIZATION FORCED WITH NO_WAIT
SELECT * FROM sys.objects WHERE object_id=3
SELECT * FROM sys.objects WHERE object_id=4
再次查看,可以看到,编译参数化了,可以在缓存中击中
一键参数化是灵丹妙药吗?
我们没有贤者之石,所以能量是守恒的。
1 一键参数化并不是无敌的,他也有不能参数化的。比如,动态sql。
2 某个时间点会出现参数嗅探,但是并不是参数化的锅