这一篇是总结Oracle的数据库管理(database),重点是其物理存储部分。图在下面,点击放大。
一些SQL命令:
手工切换重做日志文件组:
SQL> alter system switch logfile;
添加联机重做日志文件组:
SQL> alter database add logfile group 4 ('/oradata/log4a.log','/oradata/log4b.log') size 10M;
/*其中的10M是指每个日志成员文件的大小*/
添加联机重做日志文件组成员:
SQL> alter database add logfile member '/oradata/log4c.log' to group 4, '/oradata/log3c.log' to group 3;
删除联机重做日志文件组(当前组不能删除,至少保留两组):
SQL> alter database drop logfile group 4;
删除联机重做日志文件组成员(只有一个成员时不能删除):
SQL> alter database drop logfile member '/oradata/log4a.log';
清除日志文件内容:
SQL> alter database clear logfile '/oradata/log3c.log';
关于三大核心文件的几点注意事项:
1、控制文件最多可以有8个,多个控制文件最好放在不同的物理磁盘上,有一个出错时可以从其他文件恢复。
2、重做日志文件组是按顺序写、循环写。至少需要两个组,可以有多个组,每个组可以有多个成员,每个成员文件最好也放在不同的物理磁盘上。当归档模式下日志文件占用空间过大时,可以删除部分组来节省空间,但记住从控制文件中删除之后还必须删除物理文件才能腾出空间。
3、不可以删除当前正在使用的重做日志文件组,所以要删除时可手动切换(switch)当前日志文件组后再删除。
4、不可以删除重做日志文件组的最后一个成员,若要删除,可直接删除该组。
5、必须要保留两个重做日志文件组支持正常运行,如果要想删除其中一组是不可能的,但可以清除(clear)其中的数据。
一句话观点:“成功启动Oracle数据库的唯一条件是三大核心文件(控制文件、数据文件、日志文件)的检查点一致。”
今天的内容有点多,分成了两篇,这里是比较简单的第一篇,是总结安装、启动和关闭的过程,先上图,点击放大。
一些SQL命令和语句:
建立数据库时加cache子句可以把表同时建立在内存中,读取时优先读取该表:
SQL> create table emp1 cache as select * from emp;
查看内存中的表:
SQL> select table_name, cache from user_tables;
查询参数信息:
/* show parameter 参数类型;*/
SQL> show parameter process;
/* select * from v$parameter where name like '%参数类型%' */
SQL> select * from v$parameter where name like '%session%';
生成pfile和spfile:
/*从spfile生成pfile*/
SQL> create pfile from spfile;
/*从pfile生成spfile*/
SQL> create spfile from pfile;
按步骤启动数据库:
SQL> startup nomount; /*仅启动例程*/
SQL> alter database mount; /*再加载数据库*/
SQL> alter database open [read only] /*再打开数据库(只读模式)*/
启动时指定参数文件:
SQL> startup pfile='/database/initSID2.ora';
以受限方式启动:
SQL> startup restrict;
把数据库设置为受限模式:
SQL> alter system enable restricted;
修改参数文件内容:
SQL> alter system set control_files='/control01.ctl' scope=spfile;
关于Oracle的故障查找:
1、发生故障时的第一步要做的是备份当前的数据,这叫保留事故现场,以免处理故障失败时无法再现初始故障。
2、分步骤启动,确定故障所在:如果第1步失败,则要先检查共享内存、参数文件配置等;如果在第2步失败,则说明极有可能是控制文件出错,要先检查警告文件中的警告信息,恢复控制文件再说;如果在第3步失败,则要检查数据文件和日志文件是否完整可用,也可根据警告文件中的警告信息来排查故障。
3、确认三大核心文件(控制文件、数据文件、日志文件)的检查点是否一致,这是数据库能启动的唯一标志。
一句话观点:“Oracle中的绝大部分故障,都可以从启动的过程中分析出故障的原因并加以解决。”
这是我的Oracle学习笔记系列的第一篇,主要总结了Oracle的体系结构,要学好Oracle,就要先了解Oracle的运行机制和原理。把今天学习的内容整理了一个图,备忘,点击可放大。
另外,还有几个语句(sql*plus):
SYS用户登录
SQL> / as sysdba;
解锁scott用户
SQL> alter user scott identified by tiger account unlock;
切换到scott用户
SQL> conn scott/tiger;
Null值参与列计算
SQL> select ename, sal, comm, (sal+nvl(comm,0)) as salary from emp;
sql*plus中用变量查询
SQL> variable va number; SQL> declare :va:=7788; SQL> select empno,ename,sal from emp where empno = :va;
看内存中包含"select * from"的SQL语句
SQL> select sql_text from v$sqlarea where sql_text like '%select * from%';
看内存(SGA)资源情况
SQL> select * from v$sgastat;
刷内存共享池
SQL> alter system flush shared_pool;
强制重启实例
SQL> startup force;
在会话中修改日期格式
SQL> alter session set nls_date_format='yyyy-mm-dd';
如果要永久修改,windows下可在注册表中建立一个名为 nls_date_format 的DWORD值,UNIX/LINUX直接配置环境变量。
关于共享池和SQL语句同性能的关系:
由于共享池中缓存最近SQL语句的特性,所以在构建SQL语句时要特别注意,大小写不同、空格、顺序不同都会被Oracle认为是不同的SQL语句,只有完全一样的SQL语句才会从共享池中保存的SQL语句中重复使用,而由于编写SQL语句的不规范将会产生性能问题,所以要做到:
1、尽量规范SQL语句,用统一的格式,大小写、顺序、空格、缩进、变量名格式都有相应的规范并严格遵守;
2、在大量的同一格式的SQL语句查询时(比如只有where条件的值不同),应尽量使用变量,否则共享池将被大量的垃圾查询占满而使常用的SQL不被缓存,影响效率;
3、PL/SQL或者是复杂的数据查询,可以写存储过程,用带进参数的方式保证共享池中的SQL语句一致;
4、适当调整 shared_pool_size 的大小。
一句话观点:“备份和恢复数据库应该用物理备份的方式而不要用导出和导入功能,除非你的数据库数据量很少,而且都不太重要。”
参考:
Oracle的SQL语法,可以查看豆丁的这个文档:《Oracle SQL语法大全》
前一篇日志介绍了EasyASP v2.2是 如何实现防sql注入的 ,看来还是有很多人对这个年代还使用ASP报以嗤之以鼻的态度。在此还是要劝导一下,如果你认为ASP是上个世纪的东西早就过时了,何必进来让自己难受,谢谢。EasyASP只是以卑微的态度在给最后的ASPer们提供一种解决问题的新思路,我也相信还在使用ASP的人对Easp有自己的评价。
不废话了,这一篇接着介绍Easp v2.2的另一个新功能:伪URL Rewrite。
1.为什么叫伪 URL Rewrite?
这个名字听起来好像非常的别扭,不过听我先解释一下。
比如我们打开博客园首页的任意一篇文章,可以看到它的地址类似这样:
http://www.cnblogs.com/zhuqil/archive/2010/01/15/1648321.html
这个我们知道是生成了静态页。但有些网站并没有采取生成静态页的方式,而是采用了URL Rewrite技术,显示的是静态页地址,其实是在服务器端用正则表达式对页面参数进行了映射,比如下面这样:
页面地址: http://www.ambox.cn/design-2010-4.html 对应的Rewrite规则: RewriteRule /(design|stuff)-(\d+)-(\d+)\.html /?type=$1&year=$2&page=$3
这种方式也通常被称为伪静态。而EasyASP实现的,只是用ASP模仿这种伪静态,理应被称为伪伪静态,实在是不好听,所以我干脆就把它叫伪Rewrite了。这种伪Rewrite表现出来是这样的URL:
http://www.ambox.cn/?design-2010-4.html
和静态页的地址差别在哪里呢?就是多了一个 ? 号,表明它只是一个url参数来模仿静态页的地址的,事实证明这种方式确实还是对SEO有一定的帮助,比如商界blog做得比较好的 华夏智慧网 ,使用的就是这类伪Rewrite。
2.EasyASP的伪Rewrite
在Easp v2.2里,要实现伪Rewrite很简单,还是先把方法语法和参数说明列出来:
Easp.RewriteRule rule, url 用标准模式设置EasyASP的URL伪静态规则
| 参数名称 | 类型 | 说明 |
| rule | string | 含正则表达式规则的地址,必须以 "/" 或 "^/" 开头 |
| url | string | 用以映射的URL地址,必须以 "/" 开头 |
Easp.Rewrite urlpage, rule, urlparam 用高级模式设置EasyASP的URL伪静态规则
| 参数名称 | 类型 | 说明 |
| urlpage | string | 使用URL重写的页面,如是多个页面用 "|" 符号隔开;如果留空则表示当前页面 |
| rule | string | 含正则表达式规则的重写后的地址,不包含目录结构 |
| urlparam | string | 用以映射的URL参数 |
由于是模仿Rewrite,所以这里有个特殊的条件,就是只能在当前页进行URL的映射,比如你不能把/main.asp?list-45.html映射到/single.asp?t=$1&id=$2上。所以,推荐使用Easp.Rewrite来进行设置。
对于具体的使用方法,我们来看下面一段示例代码:
'设置重写规则
Easp.RewriteRule "/blog\.asp\?(\w+)/(\d{4})/(\d{2})/(\d+)\.html", "/blog.asp?username=$1&year=$2&month=$3&id=$4"
Easp.Rewrite "/|/index.asp", "(blog|photo|news)(-(\d+))?\.html", "type=$1&page=$3"
在设置了以上的规则之后,如果在浏览器里输入:
http://www.ambox.cn/blog.asp?testuser/2010/01/12345.html
效果将等同于:
http://www.ambox.cn/blog.asp?username=$1&year=2010&month=01&id=12345
这意味着你仍然可以使用 Easp.Get("username") 来获取username参数的值。同样的,你输入 /?photo-3.html ,也同样可以使用 Easp.Get("type") 来取得 "photo",用Easp.Get("page")来取得 "3"。采用这样的方式之后,对于开发者来讲就完全没有任何负担,你不需要做任何特殊的设置,即使你对重写正则不了解,在规则不生效的情况下你仍然可以通过传统的用 & 连接url参数的方式访问页面而不会产生任何程序错误。
3、EasyASP对伪URL Rewrite的补充方法
为了配合Easp的伪Rewrite,Easp还有两个方法可以减少开发者在使用伪Rewrite时编写更多的代码。
第一个方法是:
Easp.IsRewrite
这个方法可以判断当前页面访问的地址是否是已经生效的URL重写后的地址,返回一个布尔值。
第二个方法是:
Easp.ReplaceUrl param, value
| 参数名称 | 类型 | 说明 |
| param | string | URL参数名称 |
| value | string | 新的URL参数值 |
这个方法可以替换当前页URL参数中的某个值,并返回一个新的URL,比如还是用刚才的例子,用下面的地址访问:
http://www.ambox.cn/blog.asp?username=$1&year=2010&month=01&id=12345
那么,使用这个方法可以得到下面的结果
Dim newUrl
newUrl = Easp.ReplaceUrl("id","98765")
'返回 /blog.asp?username=$1&year=2010&month=01&id=98765
而用这个地址:
http://www.ambox.cn/blog.asp?testuser/2010/01/12345.html
则会返回替换后的URL重写地址
Dim newUrl
newUrl = Easp.ReplaceUrl("id","98765")
'返回 /blog.asp?testuser/2010/01/98765.html
Easp的分页中就使用了这个方法,所以在你使用Easp的URL重写后分页链接中也能准确的链接至重写后的URL地址,而使用传统的URL Rewrite组件的话,这个链接的地址是需要你自己去分析和重新拼接的。
4、小结
使用伪url重写到底有什么好处呢? 首先我可以肯定的是,对SEO是有帮助的,这个从华夏智慧网那里已经得到了印证(我和他们的开发人员深入的聊过这个问题);其次,正则中的参数匹配更严格,可以更安全的控制URL参数,比如在(\d+)的URL规则中,输入 ' 等注入带注入特征的符号肯定是不被正常解析的。EasyASP提供的这种看似简陋的伪重写模式,我相信还是有用武之地的,比如网友 智者千虑 基于 Easp v2.2 开发的 http://www.114msn.com ,其中就大量的用到了 Easp.Rewrite。
Easp v2.2的更多功能,期待你的挖掘。
下篇预告:EasyASP v2.2 的动态载入和插件模式
EasyASP终于到v2.2了,目前还在完善手册,群里有很多人问如何使用的问题,所以打算在写手册的同时写一些新功能的介绍,方便使用Easp的童鞋们快速进入状态。说实话,看到这么多人还在写ASP、还在支持Easp,觉得蛮难得的,所以也一直没有丢下Easp的开发,还是希望Easp能给最后的ASPer们一点微薄的帮助。
EasyASP v2.2的改动比较大,放弃了不少原来我觉得不好用的方法,当然,更多的是加入了不少我觉得会用得很哈屁的新功能。这是这个系列的第一篇文章,准备讲讲EasyASP是如何防范SQL注入的。
说起防SQL注入,大家肯定又是各抒己见,这篇文章也并不打算作深入的探讨,我呢就比较赞同 这篇文章 的观点,所以,我也会从这个角度来讲EasyASP v2.2是如何防范SQL注入的。
1.找到注入点
首先,还是要看看在ASP可以从哪些方面得到注入机会(以下均基于VBScript)。要使用ASP获取用户可以输入的数据,无非是这三个方面:
Request.QueryString
Request.Form
Request.Cookies
如果你的程序里没有对通过Request取得的数据进行处理而直接用在拼接的SQL语句中,则基本上无法避免SQL注入的产生。所以,必须对参与SQL拼接的值进行预先处理,但是又该如何处理呢?
2.处理注入字符
参照我比较赞同的上面那篇文章最后给出的结论,可以简单的这样理解如何处理这些可能被注入的数据:
(1) 对于字符串型的数据,处理其中的单引号,将一个单引号换为两个单引号;
(2) 对于数值型的数据,验证它是否是数字;
(3) 对于日期型的数据,验证它是否是日期型。
所以,ASP的防注入应该是在拼接SQL前对通过第1点取得的数据按第2点的方法进行交叉处理,并返回可以被正确执行在SQL语句中的字符串。
3.EasyASP的解决方案
找到了解决的办法,我们就可以着手处理了。在EasyASP v2.2里,取消了Easp.R/RQ/RF系列方法,而增加了三个方法分别对应第1点里的三种情况,它们分别是:
Easp.Get 用于获取Request.QueryString值 Easp.Post 用于获取Request.Form值 Easp.Cookie 用于获取Request.Cookies值
现在再来看看它们的语法以及它们是如何安全获取值的:
Easp.Get name[:type[separator]][:default] Easp.Post name[:type[separator]][:default] Easp.Cookie name[>subname][:type[separator]][:default]
其中,各个参数的含义如下:
| 参数名称 | 类型 | 说明 |
| name | string | 要获取的参数名,如省略其它参数则相当于原始的Request.QueryString/Form/Cookies |
| type | string | 可以是以下字符串: s - 表示name是字符串类型的值,会自动处理其中的单引号 n - 表示name是数值型的值,会验证是否为数值 d - 表示name是日期型的值,会验证是否为日期 na - 表示name是数值型的值,如果name验证不是数字,则会弹出alert警告框并返回前页 da - 表示name是日期型的值,如果name验证不是日期,则会弹出alert警告框并返回前页 ne - 表示name是数值型的值,如果name验证不是数字,则抛出用户错误信息 de - 表示name是日期型的值,如果name验证不是日期,则抛出用户错误信息 |
| separator | string | name是由此字符串(特殊符号)隔开的序列,如不省略则会逐个检查name串中的值,并返回一个数组 |
| default | string | 如果name为空或按type检查不符合数值/日期要求,则赋给此默认值;序列则逐个赋值 |
| subname | string | Easp.Cookie特有,如不省略,则获取如Request.Cookies(A)(B)的值,name为A,subname为B |
举几个例子说明一下:
Easp.Get("user") '相当于Request.QueryString("user")
Easp.Post("userid:n") '获取表单项userid的值,如果不是数字则返回空
Easp.Post("list:na, ") '获取表单序列值(用逗号", "隔开,比如用checkbox提交的),并逐个检查是否为数字,不是数字就弹出js警告框,检查都通过则返回一个数组
Easp.Get("starttime:d:2010-1-1") '以日期格式获取Url参数starttime,如果为空或不是日期则返回 "2010-1-1"
Easp.Post("allow:s|:jpg") '获取表单项allow的值,以 | 分隔,如果序列中某个值为空则给该项赋值 "jpg"
Easp.Cookie("site>power:s:null") '获取一个Cookies值,等同于安全取Request.Cookies("site")("power"), 如果为空则返回 "null"
和v2.1中的Easp.R系列相比,这几个方法既可以安全的获取需要的值,而且如果有取原始数据的需要,也可以通过不带任何其它参数得到。从语义上说,Get、Post和Cookie也更容易理解。而且通过这样处理之后,也就可以安全的用于SQL的拼接了。
4.核心代码
以下是处理程序的核心代码,其它的具体代码,可以下载EasyASP v2.2 Alpha的源文件查看。
(其中的CLeft,CRight,IsN,Has,Use等方法均为Easp的内建方法,请阅读完整源码了解其含义。另外,从下面的源码中也可以看到2.2的另一些新功能,比如支持伪URL Rewrite和Cookies的AES算法加密,以后我会陆续介绍。)
'取QueryString值,支持取Rewrite值
Function [Get](Byval s)
Dim tmp, i, arrQs, t
If Instr(s,":")>0 Then
'如果有类型参数,则取出为t
t = CRight(s,":") : s = CLeft(s,":")
End If
If isRewrite Then
'如果是Rewrite的页面地址
arrQs = Split(s_rwtU,"&")
For i = 0 To Ubound(arrQs)
If s = CLeft(arrQs(i),"=") Then
tmp = RegReplace(s_url,s_rwtS,CRight(arrQs(i),"="))
Exit For
End If
Next
Else
'否则直接取QueryString
tmp = Request.QueryString(s)
End If
[Get] = Safe(tmp,t)
End Function
'取Form值
Function Post(ByVal s)
Dim t,tmp
If Instr(s,":")>0 Then
t = CRight(s,":") : s = CLeft(s,":")
End If
tmp = Request.Form(s)
Post = Safe(tmp,t)
End Function
'取Cookies值
Function Cookie(ByVal s)
Dim p,t,coo
If Instr(s,">") > 0 Then
p = CLeft(s,">")
s = CRight(s,">")
End If
If Instr(s,":")>0 Then
t = CRight(s,":")
s = CLeft(s,":")
End If
If Has(p) And Has(s) Then
If Response.Cookies(p).HasKeys Then
coo = Request.Cookies(p)(s)
End If
ElseIf Has(s) Then
coo = Request.Cookies(s)
Else
Cookie = "" : Exit Function
End If
If IsN(coo) Then Cookie = "": Exit Function
If b_cooen Then
Use("Aes") : coo = Aes.Decode(coo)
End If
Cookie = Safe(coo,t)
End Function
'安全获取值基础方法
Function Safe(ByVal s, ByVal t)
Dim spl,d,l,li,i,tmp,arr() : l = False
'如果类型中有默认值
If Instr(t,":")>0 Then
d = CRight(t,":") : t = CLeft(t,":")
End If
If Instr(",sa,da,na,se,de,ne,", "," & Left(LCase(t),2) & ",")>0 Then
'如果有分隔符且要警告
If Len(t)>2 Then
spl = Mid(t,3) : t = LCase(Left(t,2)) : l = True
End If
ElseIf Instr("sdn", Left(LCase(t),1))>0 Then
'如果有分隔符且不警告
If Len(t)>1 Then
spl = Mid(t,2) : t = LCase(Left(t,1)) : l = True
End If
ElseIf Has(t) Then
'仅有分隔符无类型
spl = t : t = "" : l = True
End If
li = Split(s,spl)
If l Then Redim arr(Ubound(li))
For i = 0 To Ubound(li)
If i<>0 Then tmp = tmp & spl
Select Case t
Case "s","sa","se"
'字符串类型
If isN(li(i)) Then li(i) = d
tmp = tmp & Replace(li(i),"'","''")
If l Then arr(i) = Replace(li(i),"'","''")
Case "d","da","de"
'日期类型
If t = "da" Then
If Not isDate(li(i)) And Has(li(i)) Then Alert("不正确的日期值!")
ElseIf t = "de" Then
If Not isDate(li(i)) And Has(li(i)) Then [error].Throw("不正确的日期值!")
End If
tmp = IIF(isDate(li(i)), tmp & li(i), tmp & d)
If l Then arr(i) = IIF(isDate(li(i)), li(i), d)
Case "n","na","ne"
'数字类型
If t = "na" Then
If Not isNumeric(li(i)) And Has(li(i)) Then Alert("不正确的数值!")
ElseIf t = "ne" Then
If Not isNumeric(li(i)) And Has(li(i)) Then [error].Throw("不正确的数值!")
End If
tmp = IIF(isNumeric(li(i)), tmp & li(i), tmp & d)
If l Then arr(i) = IIF(isNumeric(li(i)), li(i), d)
Case Else
'未指定类型则不处理
tmp = IIF(isN(li(i)), tmp & d, tmp & li(i))
If l Then arr(i) = IIF(isN(li(i)), d, li(i))
End Select
Next
Safe = IIF(l,arr,tmp)
End Function


