MySQL数据库设计、优化

E-R模型

  • 关系型数据库建议在E-R模型基础上。我们需要根据产品经理的设计策划,抽取出来模型与关系,制定出表的结构。项目开始的第一步
  • 设计数据库软件power designer、db designer

1、E 表示entry,实体。设计实体就像是定义一个类一样,指定从哪些方面描述对象,一个实体转化为数据库中的一个表。

2、R 表示relationship,关系。关系描述两个实体之间的对应规则,关系的类型包括一对一、一对多、多对多

3、关系也是一种数据,需要通过一个字段来存储在表中

数据库

1. 数据库设计

  • 概念:

    为了建立冗余较小数据、结构合理的数据库,设计数据库时必须遵循一定的规则。在关系数据库中这种规则就是称为范式。满足第三范式的数据库设计,往往不是最好的设计。为了提高数据库的运行效率,常常需要降低范式标准:适当增加冗余,达到以空间换时间

    的目的。

  • 一范式

    基本范式,数据库表的所有的字段值都是不可分解的原子值(确保每列保持原子性

  • 二范式

    需要确保数据库表中的每一列都和主键相关,而不能只与主键的莫一部分相关(主要针对联合主键而言)。

    也就是一个数据库表中只能保存一种数据,不可以把多种数据保存在同一数据库表中。

  • 三范式

    需要确保数据库表中的每一列数据都和主键直接相关,而不是间接相关。

2. 一对多、多对一

  • 例子:员工与部门的关系
  • 设计原则:首先不要管对象之间的关系,看某个对象都有什么基本的属性。在多的一方添加外键描述数据之间的关系。在定义模型类的时候,一的一方添加关系关联键,不是实体的数据表中的字段,只用于查询数据。

3. 多对多

  • 例子:老师和学生关系

  • 设计原则:首先不要管对象之间的关系,看某个对象都有什么基本的属性。设计一个表来保存对象的基本数据。两个表之间需要一个中间表来描述数据关系。

  • 多对多对象的表:可以降低范式,增加数据的冗余,少用触发器,多用储存过程。

    出现冗余的数据,但是查询性能是好的,不需要多表查询。

4. 一对一

  • 例子:身份证管理系统
  • 设计原则:首先不要管对象之间的关系,看某个对象都有什么基本的属性。设计一个表来保存对象的基本数据。一对一的对象,有一个主从关系,主是人,从是身份证。

自连接表

  • 家族管理表系统
  • 注意: 自连接,外键列不能加非空的约束

5. 面试:无限极分类的数据表设计

  • 商品分类表

6. 数据库设计提高运行效率

  1. 在数据库物理设计时,降低范式、增加冗余、少用触发器,多用存储过程。

  2. 当计算复杂、而记录条数巨大,复杂的计算要在数据库外边。处理完成以后,最后才追加到表中。电信的计费系统设计。

  3. 发现表的记录太多,则对该表进行水平分割。

    水平分割的做法:将该表的主键PK的某个值为界线,将该表的记录水平分割为两个表。

    垂直分割的做法:若发现某个表的字段太多,例如超过八十个,则垂直分割该表,将原来的一个表分解为两个表。

7. 解决数据库高并发访问瓶颈问题

  • 优点:高可用性、自动失效切换、分布式结构、高吞吐量、低延迟、扩展性强
  • 缺点:不支持外键、占用内存大、备份和恢复不方便、重启数据节点load到内存时间长。
一、缓存式的WEB应用程序架构
  1. Web层和db层之间加一层cache层,主要目的:减少数据库读取负担,提高数据读取速度。cache存取的媒介是内存,可以考虑采用分布式的cache层,这样更容易破除内存容量的限制,同时增加了灵活性。

  2. 在使用ORM模型类查询的时候,查询集QuerySet

    从SQL的角度讲,查询集与select语句等价,过滤器像where、limit、order by子句

    • 惰性执行:创建查询集不会反问数据库,直到调用数据时,才会访问数据库。调用数据的情况:

      迭代序列化if语句的便利

    • 缓存:使用同一个查询集,第一次使用时会发生数据库的查询,然后Django会把结果缓存下来,再次使用这个查询集时会使用缓存的数据,减少了数据库的查询次数。

  3. redis数据库缓存分布式搭建

    3.1 在配置中配置搭建的redis数据库信息

    # 配置缓存redis,(不同数据配置多个缓存库)使用django-redis
    # 搭建多个缓存库,存出不同的数据类型
    CACHES = {
        "default": {
            "BACKEND": "django_redis.cache.RedisCache",
            "LOCATION": "redis://127.0.0.1:6379/0",
            "OPTIONS": {
                "CLIENT_CLASS": "django_redis.client.DefaultClient",
            }
        },
        "session": {
            "BACKEND": "django_redis.cache.RedisCache",
            "LOCATION": "redis://127.0.0.1:6379/1",
            "OPTIONS": {
                "CLIENT_CLASS": "django_redis.client.DefaultClient",
            }
        },
    }
    

    3.2 建立连接信息(基本链接redis数据库,操作数据库)

    from django_redis import get_redis_connection
    
    # 获取链接数据库的游标对象
    conn = get_redis_connection('verify_codes')
    # 根据游标对象,操作数据库,CURD
    

    3.3 使用缓存—-Django框架

    # 使用扩展 pip install drf-extensions
    # 1. 直接添加装饰器
    from rest_framework_extensions.cache.decorators import cache_response
    class AreaView(ListCreateAPIView):
        
     @cache_response(timeout=60 * 60, cache='default', key_func='calculate_cache_key')
        def get(self, request, *args, **kwargs):
            """对请求数据库的查询继承重写,装饰器,缓存数据库"""
            return super(AreaView, self).get(request, *args, **kwargs)
    
        def calculate_cache_key(self, view_instance, view_method,
                                request, args, kwargs):
            id = self.kwargs['pk']
            return '.'.join([
                str(len(args)),
                id
            ])
    '''
    cache_response装饰器接收两个参数:
    timeout:缓存时间
    cache: 缓存使用的是Django配置的redis指定数据库名。(即CACHES配置中的键的名称)
    '''
    ### 缓存数据保存位置与有效期的设置
    """想把缓存数据保存在redis中,且设置有效期,可以通过配置文件定义实现"""
    # DRF扩展
    REST_FRAMEWORK_EXTENSIONS = {
        # 缓存时间
        'DEFAULT_CACHE_RESPONSE_TIMEOUT': 60 * 60,
        # 缓存存储
        'DEFAULT_USE_CACHE': 'default',
    }
    
  4. MySQL数据库配置链接---Django框架使用

    4.1 使用MySQL数据库首先安装驱动程序

    pip install PyMySQL

    4.2 在Django的工程目录下的____init____.py文件中执行驱动

    import pymysql
    pymysql.install_as_MySQLdb()
    # 作用
    # 让Django的ORM能以mysqldb的方式来调用PyMySQL. 在程序运行以后就开始执行此驱动
    

    4.3 修改DATABASE配置信息

    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.mysql',
            'HOST': '127.0.0.1',  # 数据库主机
            'PORT': 3306,  # 数据库端口
            'USER': 'root',  # 数据库用户名
            'PASSWORD': 'mysql',  # 数据库用户密码
            'NAME': 'django_demo'  # 数据库名字
        }
    }
    
二、实现MySQL数据库异步查询实现——主要使用在Spider
  • 使用wisted是用Python实现的基于事件驱动的网络引擎框架。一种异步框架
  • Twisted 提供了 twisted.enterprise.adbapi, 遵循DB-API 2.0协议的一个异步封装。
  • adbapi 在单独的线程里面进行阻塞数据库操作, 当操作完成的时候仍然通过这个线程来进行回调。同时,原始线程能继续进行正常的工作,服务其他请求。
import pymysql
import pymysql.cursors

from twisted.enterprise import adbapi
from twisted.internet import reactor
 
 
class MysqlTwistedPipeline(object):
    def __init__(self, dbpool):
        self.dbpool = dbpool
 
    @classmethod
    def from_settings(cls, settings):
        # 需要在setting中设置数据库配置参数
        dbparms = dict(
            host=settings['MYSQL_HOST'],
            db=settings['MYSQL_DBNAME'],
            user=settings['MYSQL_USER'],
            passwd=settings['MYSQL_PASSWORD'],
            charset='utf8',
            cursorclass=pymysql.cursors.DictCursor,
            use_unicode=True,
        )
        # 连接ConnectionPool(使用MySQLdb连接,或者pymysql)
        dbpool = adbapi.ConnectionPool("pymysql", **dbparms)  # **让参数变成可变化参数
        return cls(dbpool)   # 返回实例化对象
 
    def process_item(self, item, spider):
        # 使用twisted将MySQL插入变成异步执行
        query = self.dbpool.runInteraction(self.do_insert, item)
        # 添加异常处理
        query.addCallback(self.handle_error)
 
    def handle_error(self, failure):
        # 处理异步插入时的异常
        print(failure)
 
    def do_insert(self, cursor, item):
        # 执行具体的插入
        insert_sql = """
                    insert into jobbole_artitle(name, base_url, date, comment)
                    VALUES (%s, %s, %s, %s)
                """
        cursor.execute(insert_sql, (item['name'], item['base_url'], item['date'], item['coment'],))
三、MySQL主从读写分离
  • cache层(如Memcached)只能缓解数据库的读取压力。
  • 使用主从复制技术(master-slave模式)来达到读写分离,以提高读写性能和读库的可扩展性。复制是异步进行的,所以从服务器不需要一直连接主服务器。
1. 主从同步的机制

MySQL服务器之间的主从同步基于二进制日志机制,主服务器使用二进制日志来记录数据库的变动情况。从服务器通过读取和执行日志文件来保持和主服务器的数据一致。

使用二进制日志时,主服务器的所有操作都会被记录下来,然后从服务器会接收到该日志的一个副本。从服务器可以指定执行该日志中的哪一类事件(譬如只插入数据或者只更新数据),默认会执行日志中的所有语句

主服务器和每一个从服务器都必须配置一个唯一的ID号,

2.配置主从同步的基本步骤
  1. 在主服务器上,必须开启二进制日志机制和配置一个独立的ID
  2. 在每一个从服务器上,配置一个唯一的ID,创建一个用来专门复制主服务器数据的账号
  3. 在开始复制进程前,在主服务器上记录二进制文件的位置信息
  4. 如果在开始复制之前,数据库中已经有数据,就必须先创建一个数据快照(可以使用mysqldump导出数据库,或者直接复制数据文件)
  5. 配置从服务器要连接的主服务器的IP地址和登陆授权,二进制日志文件名和位置
3. 详细配置主从同步的方法
  1. 备份主服务器原有数据到从服务器

    mysqldump -uroot -pmysql --all-databases --lock-all-tables > ~/master_db.sql
    # --lock-all-tables :执行操作时锁住所有表,防止操作时有数据修改
    # ~/master_db.sql :导出的备份数据(sql文件)位置,可自己指定
    
  2. 在从服务器上进行数据还原

    1. 将备份好的数据库包master_db.sql复制到从服务器
    2. 执行恢复数据包的命令
    mysql -uroot -pmysql < master_db.sql
    
  3. 配置主服务器master(编辑设置mysqld的配置文件,设置log_bin和server_id)

    sudo vim /etc/mysql/mysql.conf.d/mysql.cnf
    # 在配置文件中
    server—id    = 1
    log_bin  = /var/log/mysql/mysql -bin.log
    
  4. 重启Mysql服务

    sudo service mysql restart
    
  5. 登入主服务器的mysql,创建用于从服务器的同步数据的账号(主创建账号

    mysql -uroot -pmysql
    GRANT REPLICATION SLAVE ON *.* TO 'slave'@'%' identified by 'slave';
    FLUSH PRIVILEGES;
    # 命令直接执行
    
  6. 获取主服务器的二进制日志信息

    SHOW MASTER STATUS;
    # 来获取 主服务器的 日志File名、日志的位置Position
    
  7. 配置从服务器salve(找到从服务器的发MySQL的配置文件)

    # 1. 保存退出
    server—id    = 2 
    # 2. 重新启动
    sudo service mysql restart
    
  8. 进入从服务器的mysql,设置连接到master主服务器

    change master to master_host='10.211.55.5', master_user='slave', master_password='slave',master_log_file='mysql-bin.000006', master_log_pos=590;
    
    # master_host:主服务器的IP地址
    # master_log_file:前面查询到主服务器的日志文件名
    # master_log_pos:前面查询到主服务器日志文件的位置
    
  9. 开启从服务器同步,查看同步状态

    start slave # Qusery OK,
    show slave statues \G; # \G:数据库的源文件位置
    '''
    # 表示同步已经执行运行
    Slave_IO_Runnning:YES
    Slave_SQL_Running:YES
    '''
    
四、分表分库
  • 在cache层的高速缓存,MySQL的主从复制,读写分离的基础上,数量的增加,高并发会出现严重锁的问题。

    大量的高并发MySQL应用开始使用InnoDB引擎代替MyISAM。

    采用Master-Slave复制模式的MySQL架构,只能对数据库的读进行扩展,对数据的写的操作集中在Master上

1. 分表【水平拆分】
  • 对于访问极为频繁且数据量巨大的单表来说,首先要做的是减少单表的记录条数,以便减少数据查询所需的时间,提高数据库的吞吐。
  • 在分表之前,首先需要选择适当的分表策略,使得数据能够较为均衡地分布到多张表中,并且不影响正常的查询。
2. 分库【垂直拆分】
  • 分表能够解决单表数据量过大带来的查询效率下降的问题,但是却无法给数据库的并发处理能力带来质的提升。
  • 面对高并发的读写访问,当数据库master服务器无法承载写操作压力时,不管如何扩展Slave服务器都是没有意义的,对数据库进行拆分,从而提高数据库写入能力
3.分库分表策略

1、中间变量=user_id % ( 库数量 * 每个库的表数量 )

2、库=取整(中间变量 / 每个库的表数量)

3、表=中间变量 % 每个库的表数量

8. MySQL数据库存储原理

  • 存储过程是一个可编程的函数,它在数据库中创建并保存。他可以有SQL语句和一些特殊的控制结构组成。
  • 在不同的程序和平台执行相同的函数,封装特定的功能时,存储过程是相同的。可以看作对编程中面向对象的方法的模拟。

9. 数据库的优化

1、对语句的优化
  • 减少反问次数、较少表的访问行数。
  • 分开操作处理数据库操作。
  • 查询不要使用通配符、模糊查询
  • 在可能的情况下尽量限制尽量结果集行数
2、 避免使用不兼容的数据类型
  • 数据类型的不兼容可能使优化器无法执行一些本来可以进行的优化操作(将整型转化成为钱币型 )

    SELECT name FROM employee WHERE salary > 60000

    这里的数据60000为整型,转换为钱币型,不要在查询的时候强制转换

3、语句优化
  • 避免在WHERE语句对字段的函数式表达、表达式操作。
  • 避免使用 !=、<> 、IS NULL 、IN、NOT IN等。尽量使用数字型字段。
  • 使用视图加速查询、能使用Between 的就不要使用 IN。能使用DIstinct 就不使用Group by
  • 部分使用索引、不写没有意义的查询
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,711评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,932评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,770评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,799评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,697评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,069评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,535评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,200评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,353评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,290评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,331评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,020评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,610评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,694评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,927评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,330评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,904评论 2 341

推荐阅读更多精彩内容