手机浏览 RSS 2.0 订阅 膘叔的简单人生 , 腾讯云RDS购买 | 超便宜的Vultr , 注册 | 登陆
浏览模式: 标准 | 列表分类:DataBase

主键和外键的设计原则

从MYSQL4.1开始,它终于支持了外键。在数据库设计中,主键和外键是一种能够把多个表组织为一个有效的关系数据库的粘合剂。它们的设计良好与否对数据库的性能和可用性有着决定性的影响。

不得不说的是,在系统设计的时候,必须将数据库模式从理论上的逻辑设计转换为实际的物理设计。而主键和外键的结构是这个设计过程的症结所在。毕竟,一旦将所设计的数据库用于了生产环境,就很难对他们进行修改,因此,在开发阶段设计好主键和外键就显得犹为重要。

对于主键而言,它是不可被重复的,而且尽量不包含特殊含义,毕竟主键的使用只是为了让数据库多一条高效的索引,而且在被外部引用的时候不会产生重复。同样重要的是主键最好不要和其他键一起组成复合主键,而应该单独用一列来表示。

正因为主键的特殊性,所以在大多数情况下我们都采用了自增字段来表示(但由于前台应用的特殊性,建议在表字段中增加一列有唯一约束的字段,用作前台显示用。比如,原来查看新闻可能是:news.php?newsId=123,这样别人很容易猜出下一条是124,前一条是122,平时这样使用是无所谓,但如果是使用在订单上,别人就很容易知道你的订单数量等,如果采用了GUID这样的值,虽然看起来复杂了一点,但永远不会被人猜出你的订单)

当然主键也可以使用这种GUID来进行设定,但确实是不太建议使用,我主要是指MYSQL,因为MYSQL默认没有默认生成这样键值的函数。而且使用定长字符串做主键,在被外部引用的时候,就显得太长了。

而外键就相对来说比主键约束要小的多,但也是有一定规范的。比如,用主键作外键,这是大多数人的用法。即使在MYSQL尚未支持外键的时候,大多数情况下。也都是通过这样的方式来进行关联。

在使用外键的时候,一般建议是不要超过4张表关联,超过4张,可能你在某些个表里就纯粹看到的是ID记录(类似于数据库的索引),这个时候麻烦的问题就来了。该如何维护这样一个全是ID记录的表呢?一旦这个表被人误操作了。那么。所有的数据都将失去关联了。(如果数据库支持外键功能,在删除这样的外键关联数据,我记得是会出错的。)

外键,除了使用主键外,还有就是那种具有唯一索引的列,仔细想想也确实这样,如果没有唯一,那么关联出来的数据怎么能够具有唯一性?

利用外键进行数据的更新删除,应该是最方便的了。这样也可以避免数据库里会存在冗余数据。

——END——

纯粹是在这里发发牢骚,真正的表设计不是这么简单的。还要根据实际的应用,但这些可以算是一些小小的经验。也许在真正的高手眼里这些都不值一提。但,千里马常有,而伯乐不常有啊。很多人都认为是理所当然的事情,不代表我们这些新手就明白。

记录下来,作为参考,也算是一个笔记

Tags: mysql, primary key, index key, foreign key, design

Mysql动态修改参数

    mysql数据库也像ORACLE数据库一样,可以动态的修改参数,可以修改会话级变量只对当前会话产生影响;也可以修改全局变量,对所有新连接的会话都产生影响。

修改会话级变量

用show variables 命令查看当前参数的值,like 'pattern'用于模式匹配,查找指定的参数

mysql> show variables like '%sort_buffer_size%';
+---------------------------+------------+
| Variable_name             | Value      |
+---------------------------+------------+
| sort_buffer_size          | 6291448    |
+---------------------------+------------+
1 rows in set (0.00 sec)

用set SESSION命令设置会话级变量的新值

mysql> set SESSION sort_buffer_size=7000000;
Query OK, 0 rows affected (0.00 sec)

--修改会话级变量对当前会话来说立刻生效
mysql> show variables like '%sort_buffer_size%';       
+---------------------------+------------+
| Variable_name             | Value      |
+---------------------------+------------+
| sort_buffer_size          | 7000000    |
+---------------------------+------------+
1 rows in set (0.00 sec)

mysql> exit
Bye
退出重新连接后,此参数恢复原值
[root@devdbc_stb root]# mysql
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 40 to server version: 5.0.37-log

Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

mysql> show variables like '%sort_buffer_size%';       
+---------------------------+------------+
| Variable_name             | Value      |
+---------------------------+------------+
| sort_buffer_size          | 6291448    |
+---------------------------+------------+
1 rows in set (0.00 sec)

修改全局变量
[root@devdbc_stb root]# mysql
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 40 to server version: 5.0.37-log

Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

mysql> show variables like '%sort_buffer_size%';       
+---------------------------+------------+
| Variable_name             | Value      |
+---------------------------+------------+
| sort_buffer_size          | 6291448    |
+---------------------------+------------+
1 rows in set (0.00 sec)

用set GLOBAL 命令设置全局变量

mysql> set GLOBAL sort_buffer_size = 7000000;
Query OK, 0 rows affected (0.00 sec)

mysql> show variables like '%sort_buffer_size%';        
+---------------------------+------------+
| Variable_name             | Value      |
+---------------------------+------------+
| sort_buffer_size          | 6291448    |
+---------------------------+------------+
1 rows in set (0.00 sec)
当前此参数的值并不发生变化,先退出,然后重新连进去
mysql> exit
Bye
[root@devdbc_stb root]# mysql
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 41 to server version: 5.0.37-log

Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

mysql> show variables like '%sort_buffer_size%';
+---------------------------+------------+
| Variable_name             | Value      |
+---------------------------+------------+
| sort_buffer_size          | 7000000    |
+---------------------------+------------+
1 rows in set (0.00 sec)
新的参数值生效

Tags: mysql, 优化, database, 修改

mysql的大小写敏感性

  mysql的大小写敏感性主要分为两部份:一个是表名区别大小写;二是字段值不区分大小写.这个跟我们希望的恰好相反,而这却是mysql 的默认设置,不得不令人费解,而这些却是oracle数据库比较基本的东西。

   首先说说MYSQL建表的特点?如果你创建一张test表,然后你到data目录下会发现以下三个文件: 

 test.frm
 test.MYD
 test.MYI

即创建一个表,它会自动生成三个文件。这点跟oracle也有很大的不同,如果这样,那如何使用裸设备呢?看来要用mysql数据库,是不得不要用文件系统。而通常操作系统都对用户同时打开的文件数有限制,一般为1024个,使用mysql要注意.

  那如何设置让mysql的表名不区分大小写呢?修改/etc/my.cnf文件,在[mysqld] 下增加参数lower_case_table_names = 1

这个参数的含义是使所有的表名都转化成小写来处理,如果你在原系统中已有大写的表名,要先把它们重命名为小写,以免加了此参数后,以前大写的表无法识别.

   另外一个问题,就是默认的字段值不区分大小写?这点是比较令人头痛的事。如果你在一有唯一约束的列上插入两行值'A'和'a',Mysql会认为它是相同的,而在oracle中就不会。请看下面的测试:

mysql> create table test4(nick varchar(20) primary key);
Query OK, 0 rows affected (0.01 sec)

mysql> insert into test4 values('A');
Query OK, 1 row affected (0.00 sec)

mysql> insert into test4 values('a');
ERROR 1062: Duplicate entry 'a' for key 1

而如何设置让其列值区分大小写呢?

mysql> create table test4(nick varchar(20) binary primary key);
Query OK, 0 rows affected (0.01 sec)

mysql> insert into test4 values('A');
Query OK, 1 row affected (0.00 sec)

mysql> insert into test4 values('a');
Query OK, 1 row affected (0.00 sec)

在声明字符类型后,在后面加一个binary,mysql就可以区分大小写了。

Tags: mysql, database, 大小写

Mysql的一些实用字符串函数

1、concat()函数
   1.1 MySQL的concat函数可以连接一个或者多个字符串,如
       mysql> select concat('10');
       +--------------+
       | concat('10') |
       +--------------+
       | 10           |
       +--------------+
       1 row in set (0.00 sec)

       mysql> select concat('11','22','33');
       +------------------------+
       | concat('11','22','33') |
       +------------------------+
       | 112233                 |
       +------------------------+
       1 row in set (0.00 sec)

       而Oracle的concat函数只能连接两个字符串
       SQL> select concat('11','22') from dual;

   1.2 MySQL的concat函数在连接字符串的时候,只要其中一个是NULL,那么将返回NULL
       mysql> select concat('11','22',null);
       +------------------------+
       | concat('11','22',null) |
       +------------------------+
       | NULL                   |
       +------------------------+
       1 row in set (0.00 sec)

       而Oracle的concat函数连接的时候,只要有一个字符串不是NULL,就不会返回NULL
       SQL> select concat('11',NULL) from dual;

       CONCAT
       --
       11

2、concat_ws()函数, 表示concat with separator,即有分隔符的字符串连接
   如连接后以逗号分隔
       mysql> select concat_ws(',','11','22','33');
       +-------------------------------+
       | concat_ws(',','11','22','33') |
       +-------------------------------+
       | 11,22,33                      |
       +-------------------------------+
       1 row in set (0.00 sec)
  
   和concat不同的是, concat_ws函数在执行的时候,不会因为NULL值而返回NULL
       mysql> select concat_ws(',','11','22',NULL);
       +-------------------------------+
       | concat_ws(',','11','22',NULL) |
       +-------------------------------+
       | 11,22                         |
       +-------------------------------+
       1 row in set (0.00 sec)

3、group_concat()可用来行转列, Oracle没有这样的函数
   完整的语法如下
   group_concat([DISTINCT] 要连接的字段 [Order BY ASC/DESC 排序字段] [Separator '分隔符'])
   如下例子
   mysql> select * from aa;
   +------+------+
   | id   | name |
   +------+------+
   |    1 | 10   |
   |    1 | 20   |
   |    1 | 20   |
   |    2 | 20   |
   |    3 | 200  |
   |    3 | 500  |
   +------+------+
   6 rows in set (0.00 sec)
  
   3.1 以id分组,把name字段的值打印在一行,逗号分隔(默认)
       mysql> select id,group_concat(name) from aa group by id;
       +------+--------------------+
       | id   | group_concat(name) |
       +------+--------------------+
       |    1 | 10,20,20           |
       |    2 | 20                 |
       |    3 | 200,500            |
       +------+--------------------+
       3 rows in set (0.00 sec)
  
   3.2 以id分组,把name字段的值打印在一行,分号分隔
       mysql> select id,group_concat(name separator ';') from aa group by id;
       +------+----------------------------------+
       | id   | group_concat(name separator ';') |
       +------+----------------------------------+
       |    1 | 10;20;20                         |
       |    2 | 20                               |
       |    3 | 200;500                          |
       +------+----------------------------------+
       3 rows in set (0.00 sec)

   3.3 以id分组,把去冗余的name字段的值打印在一行,逗号分隔
       mysql> select id,group_concat(distinct name) from aa group by id;
       +------+-----------------------------+
       | id   | group_concat(distinct name) |
       +------+-----------------------------+
       |    1 | 10,20                       |
       |    2 | 20                          |
       |    3 | 200,500                     |
       +------+-----------------------------+
       3 rows in set (0.00 sec)

   3.4 以id分组,把name字段的值打印在一行,逗号分隔,以name排倒序
       mysql> select id,group_concat(name order by name desc) from aa group by id;
       +------+---------------------------------------+
       | id   | group_concat(name order by name desc) |
       +------+---------------------------------------+
       |    1 | 20,20,10                              |
       |    2 | 20                                    |
       |    3 | 500,200                               |
       +------+---------------------------------------+
       3 rows in set (0.00 sec)

4、repeat()函数,用来复制字符串,如下'ab'表示要复制的字符串,2表示复制的份数
   mysql> select repeat('ab',2);
   +----------------+
   | repeat('ab',2) |
   +----------------+
   | abab           |
   +----------------+
   1 row in set (0.00 sec)

   又如
   mysql> select repeat('a',2);
   +---------------+
   | repeat('a',2) |
   +---------------+
   | aa            |
   +---------------+
   1 row in set (0.00 sec)

Tags: mysql, database, 字符串

[分享]mysql数据库索引查询优化的分享

问题描述:


我们要访问的表是一个非常大的表,四千万条记录,id是主键,program_id上建了索引。
执行一条SQL:

select * from program_access_log where program_id between 1 and 4000

这条SQL非常慢。
我们原以为处理记录太多的原因,所以加了id限制,一次只读五十万条记录

select * from program_access_log where id between 1 and 500000 and program_id between 1 and 4000

但是这条SQL仍然很慢,速度比上面一条几乎没有提升。
Mysql处理50万条记录的表,条件字段还建了索引,这条语句应该是瞬间完成的。


问题分析:


这张表大约容量30G,数据库服务器内存16G,无法一次载入。就是这个造成了问题。
这条SQL有两个条件,ID一到五十万和Program_id一到四千,因为program_id范围小得多,mysql选择它做为主要索引。
先通过索引文件找出了所有program_id在1到4000范围里所有的id,这个过程非常快。
接下来要通过这些id找出表里的记录,由于这些id是离散的,所以mysql对这个表的访问不是顺序读取。
而这个表又非常大,无法一次装入内存,所以每访问一条记录mysql都要重新在磁盘上定位并把附近的记录都载入内存,大量的IO操作导致了速度的下降。

问题解决方案:
1. 以program_id为条件对表进行分区
2. 分表处理,每张表的大小不超过内存的大小
然而,服务器用的是mysql5.0,不支持分区,而且这个表是公共表,无法在不影响其它项目的条件下修改表的结构。
所以我们采取了第三种办法:

select * from program_access_log where id between 1 and 500000 and program_id between 1 and 15000000

现在program_id的范围远大于id的范围,id被当做主要索引进行查找,由于id是主键,所以查找的是连续50万条记录,速度和访问一个50万条记录的表基本一样

总结:
这是一个在千万笔记录表中由于使用了索引导致了数据查找变慢的问题,有一定的典型性和大家交流下!

————END————

索引这个东西很害人的。好的时候很好。。。用的不好。那真的是害己害人啊。

本来来自:PHP5研究室,责编:抽烟的蚊子,网址:http://www.phpv.net/html/1624.html

Tags: mysql, database, 索引, 优化, 技巧